From 18e8feac8f08c97811837e1282ffbec0fffe6579 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 27 Mar 2019 15:54:04 -0400 Subject: [PATCH 1/3] SILGen: Kill OpaqueValueState and clean up code for opening existentials OpaqueValueState used to store a SILValue, so back then the IsConsumable flag was meaningful. But now we can just check if the ManagedValue has a cleanup or not. Also, we were passing around an opened ArchetypeType for no good reason. --- lib/SILGen/SILGenBridging.cpp | 6 +-- lib/SILGen/SILGenConvert.cpp | 69 ++++++++--------------------------- lib/SILGen/SILGenExpr.cpp | 26 +++++-------- lib/SILGen/SILGenFunction.h | 28 +++++--------- lib/SILGen/SILGenLValue.cpp | 7 +--- lib/SILGen/SILGenPoly.cpp | 51 ++++++-------------------- 6 files changed, 49 insertions(+), 138 deletions(-) diff --git a/lib/SILGen/SILGenBridging.cpp b/lib/SILGen/SILGenBridging.cpp index 2785eb9116f63..495e17786d03d 100644 --- a/lib/SILGen/SILGenBridging.cpp +++ b/lib/SILGen/SILGenBridging.cpp @@ -703,11 +703,11 @@ static ManagedValue emitNativeToCBridgedNonoptionalValue(SILGenFunction &SGF, FormalEvaluationScope scope(SGF); - auto openedExistential = SGF.emitOpenExistential( - loc, v, openedType, SGF.getLoweredType(openedType), + v = SGF.emitOpenExistential( + loc, v, SGF.getLoweredType(openedType), AccessKind::Read); + v = v.ensurePlusOne(SGF, loc); - v = SGF.manageOpaqueValue(openedExistential, loc, SGFContext()); nativeType = openedType; } diff --git a/lib/SILGen/SILGenConvert.cpp b/lib/SILGen/SILGenConvert.cpp index a7b5effb44adb..d7edf63093b01 100644 --- a/lib/SILGen/SILGenConvert.cpp +++ b/lib/SILGen/SILGenConvert.cpp @@ -923,27 +923,20 @@ ManagedValue SILGenFunction::emitProtocolMetatypeToObject(SILLocation loc, return emitManagedRValueWithCleanup(value); } -SILGenFunction::OpaqueValueState +ManagedValue SILGenFunction::emitOpenExistential( SILLocation loc, ManagedValue existentialValue, - ArchetypeType *openedArchetype, SILType loweredOpenedType, AccessKind accessKind) { assert(isInFormalEvaluationScope()); - // Open the existential value into the opened archetype value. - bool isUnique = true; - bool canConsume; - ManagedValue archetypeMV; - SILType existentialType = existentialValue.getType(); switch (existentialType.getPreferredExistentialRepresentation(SGM.M)) { case ExistentialRepresentation::Opaque: { // With CoW existentials we can't consume the boxed value inside of // the existential. (We could only do so after a uniqueness check on // the box holding the value). - canConsume = false; if (existentialType.isAddress()) { OpenedExistentialAccess allowedAccess = getOpenedExistentialAccessFor(accessKind); @@ -956,28 +949,20 @@ SILGenFunction::emitOpenExistential( SILValue archetypeValue = B.createOpenExistentialAddr(loc, existentialValue.getValue(), loweredOpenedType, allowedAccess); - archetypeMV = ManagedValue::forUnmanaged(archetypeValue); + return ManagedValue::forUnmanaged(archetypeValue); } else { // borrow the existential and return an unmanaged opened value. - archetypeMV = getBuilder().createOpenExistentialValue( + return B.createOpenExistentialValue( loc, existentialValue, loweredOpenedType); } - break; } case ExistentialRepresentation::Metatype: assert(existentialType.isObject()); - archetypeMV = B.createOpenExistentialMetatype( + return B.createOpenExistentialMetatype( loc, existentialValue, loweredOpenedType); - // Metatypes are always trivial. Consuming would be a no-op. - canConsume = false; - break; - case ExistentialRepresentation::Class: { + case ExistentialRepresentation::Class: assert(existentialType.isObject()); - archetypeMV = - B.createOpenExistentialRef(loc, existentialValue, loweredOpenedType); - canConsume = archetypeMV.hasCleanup(); - break; - } + return B.createOpenExistentialRef(loc, existentialValue, loweredOpenedType); case ExistentialRepresentation::Boxed: if (existentialType.isAddress()) { existentialValue = emitLoad(loc, existentialValue.getValue(), @@ -989,67 +974,43 @@ SILGenFunction::emitOpenExistential( existentialType = existentialValue.getType(); assert(existentialType.isObject()); if (loweredOpenedType.isAddress()) { - archetypeMV = ManagedValue::forUnmanaged( + return ManagedValue::forUnmanaged( B.createOpenExistentialBox(loc, existentialValue.getValue(), loweredOpenedType)); } else { assert(!silConv.useLoweredAddresses()); - archetypeMV = getBuilder().createOpenExistentialBoxValue( + return B.createOpenExistentialBoxValue( loc, existentialValue, loweredOpenedType); } - // NB: Don't forward the cleanup, because consuming a boxed value won't - // consume the box reference. - // The boxed value can't be assumed to be uniquely referenced. - // We can never consume it. - // TODO: We could use isUniquelyReferenced to shorten the duration of - // the box to the point that the opaque value is copied out. - isUnique = false; - canConsume = false; - break; case ExistentialRepresentation::None: llvm_unreachable("not existential"); } - - assert(!canConsume || isUnique); (void) isUnique; - - return SILGenFunction::OpaqueValueState{ - archetypeMV, - /*isConsumable*/ canConsume, - /*hasBeenConsumed*/ false - }; } -ManagedValue SILGenFunction::manageOpaqueValue(OpaqueValueState &entry, +ManagedValue SILGenFunction::manageOpaqueValue(ManagedValue value, SILLocation loc, SGFContext C) { // If the opaque value is consumable, we can just return the // value with a cleanup. There is no need to retain it separately. - if (entry.IsConsumable) { - assert(!entry.HasBeenConsumed - && "Uniquely-referenced opaque value already consumed"); - entry.HasBeenConsumed = true; - return entry.Value; - } - - assert(!entry.Value.hasCleanup()); + if (value.hasCleanup()) + return value; // If the context wants a +0 value, guaranteed or immediate, we can // give it to them, because OpenExistential emission guarantees the // value. - if (C.isGuaranteedPlusZeroOk()) { - return entry.Value; - } + if (C.isGuaranteedPlusZeroOk()) + return value; // If the context has an initialization a buffer, copy there instead // of making a temporary allocation. if (auto I = C.getEmitInto()) { - I->copyOrInitValueInto(*this, loc, entry.Value, /*init*/ false); + I->copyOrInitValueInto(*this, loc, value, /*init*/ false); I->finishInitialization(*this); return ManagedValue::forInContext(); } // Otherwise, copy the value into a temporary. - return entry.Value.copyUnmanaged(*this, loc); + return value.copyUnmanaged(*this, loc); } ManagedValue SILGenFunction::emitConvertedRValue(Expr *E, diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index ec54ced0d3e1f..425e80cbdb996 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -2436,15 +2436,11 @@ emitKeyPathRValueBase(SILGenFunction &subSGF, baseType = opened->getCanonicalType(); auto openedOpaqueValue = subSGF.emitOpenExistential(loc, paramSubstValue, - opened, subSGF.getLoweredType(baseType), + subSGF.getLoweredType(baseType), AccessKind::Read); // Maybe we could peephole this if we know the property load can borrow the // base value… - if (!openedOpaqueValue.IsConsumable) { - paramSubstValue = openedOpaqueValue.Value.copyUnmanaged(subSGF, loc); - } else { - paramSubstValue = openedOpaqueValue.Value; - } + paramSubstValue = openedOpaqueValue.ensurePlusOne(subSGF, loc); } // Upcast a class instance to the property's declared type if necessary. @@ -4830,14 +4826,14 @@ void SILGenFunction::emitOpenExistentialExprImpl( SGFContext::AllowGuaranteedPlusZero); Type opaqueValueType = E->getOpaqueValue()->getType()->getRValueType(); - auto state = emitOpenExistential( - E, existentialValue, E->getOpenedArchetype(), + auto payload = emitOpenExistential( + E, existentialValue, getLoweredType(opaqueValueType), AccessKind::Read); // Register the opaque value for the projected existential. SILGenFunction::OpaqueValueRAII opaqueValueRAII( - *this, E->getOpaqueValue(), state); + *this, E->getOpaqueValue(), payload); emitSubExpr(E->getSubExpr()); } @@ -4867,13 +4863,9 @@ RValue RValueEmitter::visitMakeTemporarilyEscapableExpr( auto visitSubExpr = [&](ManagedValue escapingClosure, bool isClosureConsumable) -> RValue { // Bind the opaque value to the escaping function. - SILGenFunction::OpaqueValueState opaqueValue{ - escapingClosure, - /*consumable*/ isClosureConsumable, - /*hasBeenConsumed*/ false, - }; + assert(isClosureConsumable == escapingClosure.hasCleanup()); SILGenFunction::OpaqueValueRAII pushOpaqueValue(SGF, E->getOpaqueValue(), - opaqueValue); + escapingClosure); // Emit the guarded expression. return visit(E->getSubExpr(), C); @@ -4907,8 +4899,8 @@ RValue RValueEmitter::visitMakeTemporarilyEscapableExpr( RValue RValueEmitter::visitOpaqueValueExpr(OpaqueValueExpr *E, SGFContext C) { assert(SGF.OpaqueValues.count(E) && "Didn't bind OpaqueValueExpr"); - auto &entry = SGF.OpaqueValues[E]; - return RValue(SGF, E, SGF.manageOpaqueValue(entry, E, C)); + auto value = SGF.OpaqueValues[E]; + return RValue(SGF, E, SGF.manageOpaqueValue(value, E, C)); } ProtocolDecl *SILGenFunction::getPointerProtocol() { diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 2948e21f1a513..d70276bf58917 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -971,28 +971,19 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction CanType inputTy, SILType resultTy); - struct OpaqueValueState { - ManagedValue Value; - bool IsConsumable; - bool HasBeenConsumed; - }; - - ManagedValue manageOpaqueValue(OpaqueValueState &entry, + ManagedValue manageOpaqueValue(ManagedValue value, SILLocation loc, SGFContext C); /// Open up the given existential value and project its payload. /// /// \param existentialValue The existential value. - /// \param openedArchetype The opened existential archetype. /// \param loweredOpenedType The lowered type of the projection, which in /// practice will be the openedArchetype, possibly wrapped in a metatype. - OpaqueValueState - emitOpenExistential(SILLocation loc, - ManagedValue existentialValue, - ArchetypeType *openedArchetype, - SILType loweredOpenedType, - AccessKind accessKind); + ManagedValue emitOpenExistential(SILLocation loc, + ManagedValue existentialValue, + SILType loweredOpenedType, + AccessKind accessKind); /// Wrap the given value in an existential container. /// @@ -1544,9 +1535,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction emitOpenExistentialExprImpl(e, emitSubExpr); } - /// Mapping from active opaque value expressions to their values, - /// along with a bit for each indicating whether it has been consumed yet. - llvm::SmallDenseMap + /// Mapping from active opaque value expressions to their values. + llvm::SmallDenseMap OpaqueValues; /// A mapping from opaque value expressions to the open-existential @@ -1568,11 +1558,11 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction public: OpaqueValueRAII(SILGenFunction &self, OpaqueValueExpr *opaqueValue, - OpaqueValueState state) + ManagedValue value) : Self(self), OpaqueValue(opaqueValue) { assert(Self.OpaqueValues.count(OpaqueValue) == 0 && "Opaque value already has a binding"); - Self.OpaqueValues[OpaqueValue] = state; + Self.OpaqueValues[OpaqueValue] = value; } ~OpaqueValueRAII(); diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 022457f7e6d6c..f1dbaac55e2f7 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -2731,14 +2731,11 @@ LValue SILGenLValue::visitOpaqueValueExpr(OpaqueValueExpr *e, } assert(SGF.OpaqueValues.count(e) && "Didn't bind OpaqueValueExpr"); - - auto &entry = SGF.OpaqueValues.find(e)->second; - assert(!entry.HasBeenConsumed && "opaque value already consumed"); - entry.HasBeenConsumed = true; + auto value = SGF.OpaqueValues[e]; RegularLocation loc(e); LValue lv; - lv.add(entry.Value.formalAccessBorrow(SGF, loc), None, + lv.add(value.formalAccessBorrow(SGF, loc), None, getValueTypeData(SGF, accessKind, e)); return lv; } diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index 2505312feb26a..3711f80834bb7 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -182,12 +182,6 @@ collectExistentialConformances(ModuleDecl *M, CanType fromType, CanType toType) return M->getASTContext().AllocateCopy(conformances); } -static ArchetypeType *getOpenedArchetype(CanType openedType) { - while (auto metatypeTy = dyn_cast(openedType)) - openedType = metatypeTy.getInstanceType(); - return cast(openedType); -} - static ManagedValue emitTransformExistential(SILGenFunction &SGF, SILLocation loc, ManagedValue input, @@ -198,17 +192,11 @@ static ManagedValue emitTransformExistential(SILGenFunction &SGF, FormalEvaluationScope scope(SGF); - SILGenFunction::OpaqueValueState state; - ArchetypeType *openedArchetype = nullptr; - if (inputType->isAnyExistentialType()) { CanType openedType = OpenedArchetypeType::getAny(inputType); SILType loweredOpenedType = SGF.getLoweredType(openedType); - // Unwrap zero or more metatype levels - openedArchetype = getOpenedArchetype(openedType); - - state = SGF.emitOpenExistential(loc, input, openedArchetype, + input = SGF.emitOpenExistential(loc, input, loweredOpenedType, AccessKind::Read); inputType = openedType; } @@ -235,20 +223,12 @@ static ManagedValue emitTransformExistential(SILGenFunction &SGF, AbstractionPattern opaque = AbstractionPattern::getOpaque(); const TypeLowering &concreteTL = SGF.getTypeLowering(opaque, inputType); const TypeLowering &expectedTL = SGF.getTypeLowering(outputType); - input = SGF.emitExistentialErasure( + return SGF.emitExistentialErasure( loc, inputType, concreteTL, expectedTL, conformances, ctxt, [&](SGFContext C) -> ManagedValue { - if (openedArchetype) - return SGF.manageOpaqueValue(state, loc, C); - if (input.isPlusOne(SGF)) - return input; - if (C.isGuaranteedPlusZeroOk()) - return input; - return input.copyUnmanaged(SGF, loc); + return SGF.manageOpaqueValue(input, loc, C); }); - - return input; } /// Apply this transformation to an arbitrary value. @@ -593,15 +573,12 @@ ManagedValue Transform::transform(ManagedValue v, CanType openedType = OpenedArchetypeType::getAny(inputSubstType); SILType loweredOpenedType = SGF.getLoweredType(openedType); - // Unwrap zero or more metatype levels - auto openedArchetype = getOpenedArchetype(openedType); - FormalEvaluationScope scope(SGF); - auto state = SGF.emitOpenExistential(Loc, v, openedArchetype, - loweredOpenedType, - AccessKind::Read); - auto payload = SGF.manageOpaqueValue(state, Loc, SGFContext()); + auto payload = SGF.emitOpenExistential(Loc, v, + loweredOpenedType, + AccessKind::Read); + payload = payload.ensurePlusOne(SGF, Loc); return transform(payload, AbstractionPattern::getOpaque(), openedType, @@ -3745,17 +3722,11 @@ emitOpenExistentialInSelfConformance(SILGenFunction &SGF, SILLocation loc, SILDeclRef witness, SubstitutionMap subs, ManagedValue value, SILParameterInfo destParameter) { - auto typeAndConf = getSelfTypeAndConformanceForWitness(witness, subs); - auto archetype = typeAndConf.first->castTo(); - assert(archetype->isOpenedExistential()); - auto openedTy = destParameter.getSILStorageType(); - auto state = SGF.emitOpenExistential(loc, value, archetype, openedTy, - destParameter.isIndirectMutating() - ? AccessKind::ReadWrite - : AccessKind::Read); - - return state.Value; + return SGF.emitOpenExistential(loc, value, openedTy, + destParameter.isIndirectMutating() + ? AccessKind::ReadWrite + : AccessKind::Read); } void SILGenFunction::emitProtocolWitness(AbstractionPattern reqtOrigTy, From ab81406711222ff8c88717ef456a5e5acc575b98 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 27 Mar 2019 17:10:22 -0400 Subject: [PATCH 2/3] Sema: Use coerceToRValue() to load tuples of lvalues instead of coerceTupleToTuple() --- lib/Sema/CSApply.cpp | 12 +++++------- lib/Sema/TypeCheckConstraints.cpp | 10 +++++----- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 94632cc840374..caa54171a8f0f 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -5259,13 +5259,7 @@ Expr *ExprRewriter::coerceExistential(Expr *expr, Type toType, // Load tuples with lvalue elements. if (auto tupleType = fromType->getAs()) { if (tupleType->hasLValueType()) { - auto toTuple = tupleType->getRValueType()->castTo(); - SmallVector sources; - bool failed = computeTupleShuffle(tupleType, toTuple, sources); - assert(!failed && "Couldn't convert tuple to tuple?"); - (void)failed; - - coerceTupleToTuple(expr, tupleType, toTuple, locator, sources); + expr = cs.coerceToRValue(expr); } } @@ -6492,6 +6486,10 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, auto toTuple = toType->getAs(); if (!toTuple) break; + + if (fromTuple->hasLValueType() && !toTuple->hasLValueType()) + return coerceToType(cs.coerceToRValue(expr), toType, locator); + SmallVector sources; if (!computeTupleShuffle(fromTuple, toTuple, sources)) { return coerceTupleToTuple(expr, fromTuple, toTuple, diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 6506e49d37e74..f8b769629eff8 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -3134,15 +3134,11 @@ Expr *TypeChecker::coerceToRValue(Expr *expr, return FVE; } - // Load lvalues. - if (exprTy->is()) - return addImplicitLoadExpr(expr, getType, setType); - // Walk into parenthesized expressions to update the subexpression. if (auto paren = dyn_cast(expr)) { auto sub = coerceToRValue(paren->getSubExpr(), getType, setType); paren->setSubExpr(sub); - setType(paren, getType(sub)); + setType(paren, ParenType::get(Context, getType(sub))); return paren; } @@ -3186,6 +3182,10 @@ Expr *TypeChecker::coerceToRValue(Expr *expr, return tuple; } + // Load lvalues. + if (exprTy->is()) + return addImplicitLoadExpr(expr, getType, setType); + // Nothing to do. return expr; } From e2c9c52c93a5a1361de61ae864a5b6d66a57de7d Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 27 Mar 2019 17:10:05 -0400 Subject: [PATCH 3/3] AST/Sema/SILGen: Implement tuple conversions 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 --- include/swift/AST/DiagnosticsSema.def | 7 - include/swift/AST/Expr.h | 61 ++++--- include/swift/AST/ExprNodes.def | 2 +- lib/AST/ASTDumper.cpp | 19 +- lib/AST/ASTVerifier.cpp | 48 +++-- lib/AST/ASTWalker.cpp | 14 +- lib/AST/Expr.cpp | 19 +- lib/SILGen/SILGenExpr.cpp | 91 +++------- lib/Sema/CSApply.cpp | 249 ++++++++------------------ lib/Sema/MiscDiagnostics.cpp | 4 +- test/Constraints/members.swift | 1 - test/Constraints/protocols.swift | 2 - test/Constraints/tuple.swift | 29 +++ test/SILGen/tuples.swift | 87 ++++++--- test/Sema/diag_express_tuple.swift | 19 -- 15 files changed, 295 insertions(+), 357 deletions(-) delete mode 100644 test/Sema/diag_express_tuple.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 83f6653e043dd..56c2d0e2ed94a 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -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 //------------------------------------------------------------------------------ diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 2fe2b97aa0e32..3beb297719589 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -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, @@ -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 { +/// 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 { friend TrailingObjects; - size_t numTrailingObjects(OverloadToken) const { - return Bits.TupleShuffleExpr.NumElementMappings; + size_t numTrailingObjects(OverloadToken) const { + return Bits.DestructureTupleExpr.NumElements; } private: - TupleShuffleExpr(Expr *subExpr, ArrayRef elementMapping, - Type ty) - : ImplicitConversionExpr(ExprKind::TupleShuffle, subExpr, ty) { - Bits.TupleShuffleExpr.NumElementMappings = elementMapping.size(); - std::uninitialized_copy(elementMapping.begin(), elementMapping.end(), - getTrailingObjects()); + Expr *DstExpr; + + DestructureTupleExpr(ArrayRef 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()); } public: - static TupleShuffleExpr *create(ASTContext &ctx, Expr *subExpr, - ArrayRef 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 destructuredElements, + Expr *srcExpr, Expr *dstExpr, Type ty); - ArrayRef getElementMapping() const { - return {getTrailingObjects(), - static_cast(Bits.TupleShuffleExpr.NumElementMappings)}; + ArrayRef getDestructuredElements() const { + return {getTrailingObjects(), + static_cast(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; } }; diff --git a/include/swift/AST/ExprNodes.def b/include/swift/AST/ExprNodes.def index 98dce858b3d43..c5d6866f5f10c 100644 --- a/include/swift/AST/ExprNodes.def +++ b/include/swift/AST/ExprNodes.def @@ -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) diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 39611a53467ec..62e5ddfb035f6 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -2138,15 +2138,20 @@ class PrintExpr : public ExprVisitor { 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) { diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index a285ce696c186..1248b281a5c06 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -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))) + 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))) @@ -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() ->getElementType(i)); }; - /// Retrieve the ith element type from the resulting tuple type. - auto getOuterElementType = [&](unsigned i) -> Type { - return E->getType()->castTo()->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); } diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index 82a020d62ddb2..e299778e5a260 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -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. // @@ -639,9 +639,15 @@ class Traversal : public ASTVisitorgetSubExpr())) { - 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; } diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 17f9082915ecd..8e960f7b7aff1 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -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); @@ -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: @@ -1333,13 +1333,14 @@ CaptureListExpr *CaptureListExpr::create(ASTContext &ctx, return ::new(mem) CaptureListExpr(captureList, closureBody); } -TupleShuffleExpr *TupleShuffleExpr::create(ASTContext &ctx, - Expr *subExpr, - ArrayRef elementMapping, - Type ty) { - auto size = totalSizeToAlloc(elementMapping.size()); - auto mem = ctx.Allocate(size, alignof(TupleShuffleExpr)); - return ::new(mem) TupleShuffleExpr(subExpr, elementMapping, ty); +DestructureTupleExpr * +DestructureTupleExpr::create(ASTContext &ctx, + ArrayRef destructuredElements, + Expr *srcExpr, Expr *dstExpr, Type ty) { + auto size = totalSizeToAlloc(destructuredElements.size()); + auto mem = ctx.Allocate(size, alignof(DestructureTupleExpr)); + return ::new(mem) DestructureTupleExpr(destructuredElements, + srcExpr, dstExpr, ty); } ArgumentShuffleExpr *ArgumentShuffleExpr::create(ASTContext &ctx, diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 425e80cbdb996..c0db5ad81e6ee 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -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); @@ -2151,81 +2151,34 @@ RValue SILGenFunction::emitApplyOfStoredPropertyInitializer( subs, {}, calleeTypeInfo, ApplyOptions::None, C); } -static void emitTupleShuffleExprInto(RValueEmitter &emitter, - TupleShuffleExpr *E, - Initialization *outerTupleInit) { - CanTupleType outerTuple = cast(E->getType()->getCanonicalType()); - auto outerFields = outerTuple->getElements(); - (void) outerFields; - - // Decompose the initialization. - SmallVector 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(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 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 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()->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; } diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index caa54171a8f0f..b1ea21bee9d29 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -380,14 +380,10 @@ namespace { /// /// \param sources The sources of each of the elements to be used in the /// resulting tuple, as provided by \c computeTupleShuffle. - /// - /// \param typeFromPattern Optionally, the caller can specify the pattern - /// from where the toType is derived, so that we can deliver better fixit. Expr *coerceTupleToTuple(Expr *expr, TupleType *fromTuple, TupleType *toTuple, ConstraintLocatorBuilder locator, - SmallVectorImpl &sources, - Optional typeFromPattern = None); + ArrayRef sources); /// Coerce a subclass, class-constrained archetype, class-constrained /// existential or to a superclass type. @@ -4841,56 +4837,6 @@ findCalleeDeclRef(ConstraintSystem &cs, const Solution &solution, return solution.resolveLocatorToDecl(locator); } -static bool -shouldApplyAddingLabelFixit(TuplePattern *tuplePattern, TupleType *fromTuple, - TupleType *toTuple, - std::vector> &locInsertPairs) { - std::vector patternParts; - std::vector fromParts; - std::vector toParts; - patternParts.push_back(tuplePattern); - fromParts.push_back(fromTuple); - toParts.push_back(toTuple); - while (!patternParts.empty()) { - TuplePattern *curPattern = patternParts.back(); - TupleType *curFrom = fromParts.back(); - TupleType *curTo = toParts.back(); - patternParts.pop_back(); - fromParts.pop_back(); - toParts.pop_back(); - unsigned n = curPattern->getElements().size(); - if (curFrom->getElements().size() != n || - curTo->getElements().size() != n) - return false; - for (unsigned i = 0; i < n; i++) { - Pattern* subPat = curPattern->getElement(i).getPattern(); - const TupleTypeElt &subFrom = curFrom->getElement(i); - const TupleTypeElt &subTo = curTo->getElement(i); - if ((subFrom.getType()->getKind() == TypeKind::Tuple) ^ - (subTo.getType()->getKind() == TypeKind::Tuple)) - return false; - auto addLabelFunc = [&]() { - if (subFrom.getName().empty() && !subTo.getName().empty()) { - llvm::SmallString<8> Name; - Name.append(subTo.getName().str()); - Name.append(": "); - locInsertPairs.push_back({subPat->getStartLoc(), Name.str()}); - } - }; - if (auto subFromTuple = subFrom.getType()->getAs()) { - fromParts.push_back(subFromTuple); - toParts.push_back(subTo.getType()->getAs()); - patternParts.push_back(static_cast(subPat)); - addLabelFunc(); - } else if (subFrom.getType()->isEqual(subTo.getType())) { - addLabelFunc(); - } else - return false; - } - } - return true; -} - /// Produce the caller-side default argument for this default argument, or /// null if the default argument will be provided by the callee. static std::pair @@ -4974,155 +4920,102 @@ getCallerDefaultArg(ConstraintSystem &cs, DeclContext *dc, return {init, param->getDefaultArgumentKind()}; } -static Expr *lookThroughIdentityExprs(Expr *expr) { - while (true) { - if (auto ident = dyn_cast(expr)) { - expr = ident->getSubExpr(); - } else if (auto anyTry = dyn_cast(expr)) { - if (isa(anyTry)) - return expr; - expr = anyTry->getSubExpr(); - } else { - return expr; - } - } -} - -/// Rebuild the ParenTypes for the given expression, whose underlying expression -/// should be set to the given type. This has to apply to exactly the same -/// levels of sugar that were stripped off by lookThroughIdentityExprs. -static Type rebuildIdentityExprs(ConstraintSystem &cs, Expr *expr, Type type) { - ASTContext &ctx = cs.getASTContext(); - if (auto paren = dyn_cast(expr)) { - type = rebuildIdentityExprs(cs, paren->getSubExpr(), type); - cs.setType(paren, ParenType::get(ctx, type->getInOutObjectType(), - ParameterTypeFlags().withInOut(type->is()))); - return cs.getType(paren); - } - - if (auto ident = dyn_cast(expr)) { - type = rebuildIdentityExprs(cs, ident->getSubExpr(), type); - cs.setType(ident, type); - return cs.getType(ident); - } - - if (auto ident = dyn_cast(expr)) { - if (isa(ident)) - return type; +static bool canPeepholeTupleConversion(Expr *expr, + ArrayRef sources) { + if (!isa(expr)) + return false; - type = rebuildIdentityExprs(cs, ident->getSubExpr(), type); - cs.setType(ident, type); - return cs.getType(ident); + for (unsigned i = 0, e = sources.size(); i != e; ++i) { + if (sources[i] != i) + return false; } - return type; + return true; } -Expr *ExprRewriter::coerceTupleToTuple(Expr *expr, TupleType *fromTuple, +Expr *ExprRewriter::coerceTupleToTuple(Expr *expr, + TupleType *fromTuple, TupleType *toTuple, ConstraintLocatorBuilder locator, - SmallVectorImpl &sources, - Optional typeFromPattern){ + ArrayRef sources) { auto &tc = cs.getTypeChecker(); - // Capture the tuple expression, if there is one. - Expr *innerExpr = lookThroughIdentityExprs(expr); - auto *fromTupleExpr = dyn_cast(innerExpr); + // If the input expression is a tuple expression, we can convert it in-place. + if (canPeepholeTupleConversion(expr, sources)) { + auto *tupleExpr = cast(expr); - /// Check each of the tuple elements in the destination. - bool anythingShuffled = false; - SmallVector toSugarFields; - SmallVector fromTupleExprFields( - fromTuple->getElements().size()); + for (unsigned i = 0, e = tupleExpr->getNumElements(); i != e; ++i) { + auto *fromElt = tupleExpr->getElement(i); - for (unsigned i = 0, n = toTuple->getNumElements(); i != n; ++i) { - const auto &toElt = toTuple->getElement(i); - auto toEltType = toElt.getType(); + // Actually convert the source element. + auto toEltType = toTuple->getElementType(i); - // If the source and destination index are different, we'll be shuffling. - if (sources[i] != i) { - anythingShuffled = true; - } - - // We're matching one element to another. If the types already - // match, there's nothing to do. - const auto &fromElt = fromTuple->getElement(sources[i]); - auto fromEltType = fromElt.getType(); - if (fromEltType->isEqual(toEltType)) { - // Get the sugared type directly from the tuple expression, if there - // is one. - if (fromTupleExpr) - fromEltType = cs.getType(fromTupleExpr->getElement(sources[i])); + auto *toElt + = coerceToType(fromElt, toEltType, + locator.withPathElement( + LocatorPathElt::getTupleElement(i))); + if (!toElt) + return nullptr; - toSugarFields.push_back(toElt.getWithType(fromEltType)); - fromTupleExprFields[sources[i]] = fromElt; - continue; + tupleExpr->setElement(i, toElt); } - // We need to convert the source element to the destination type. - if (!fromTupleExpr) { - // FIXME: Lame! We can't express this in the AST. - auto anchorExpr = locator.getBaseLocator()->getAnchor(); - InFlightDiagnostic diag = tc.diagnose(anchorExpr->getLoc(), - diag::tuple_conversion_not_expressible, - fromTuple, toTuple); - if (typeFromPattern) { - std::vector> locInsertPairs; - auto *tupleP = dyn_cast(typeFromPattern.getValue()); - if (tupleP && shouldApplyAddingLabelFixit(tupleP, toTuple, fromTuple, - locInsertPairs)) { - for (auto &Pair : locInsertPairs) { - diag.fixItInsert(Pair.first, Pair.second); - } - } - } - return nullptr; - } + tupleExpr->setType(toTuple); + cs.cacheType(tupleExpr); - // Actually convert the source element. - auto convertedElt - = coerceToType(fromTupleExpr->getElement(sources[i]), toEltType, - locator.withPathElement( - LocatorPathElt::getTupleElement(sources[i]))); - if (!convertedElt) - return nullptr; - - fromTupleExpr->setElement(sources[i], convertedElt); - - // Record the sugared field name. - toSugarFields.push_back(toElt.getWithType(cs.getType(convertedElt))); - fromTupleExprFields[sources[i]] = - fromElt.getWithType(cs.getType(convertedElt)); + return tupleExpr; } - // Compute the updated 'from' tuple type, since we may have - // performed some conversions in place. - Type fromTupleType = TupleType::get(fromTupleExprFields, tc.Context); - if (fromTupleExpr) { - cs.setType(fromTupleExpr, fromTupleType); - - // Update the types of parentheses around the tuple expression. - rebuildIdentityExprs(cs, expr, fromTupleType); + // Build a list of OpaqueValueExprs that matches the structure + // of expr's type. + // + // Each OpaqueValueExpr's type is equal to the type of the + // corresponding element of fromTuple. + SmallVector destructured; + for (unsigned i = 0, e = sources.size(); i != e; ++i) { + auto fromEltType = fromTuple->getElementType(i); + auto *opaqueElt = new (tc.Context) OpaqueValueExpr(expr->getLoc(), + fromEltType); + cs.cacheType(opaqueElt); + destructured.push_back(opaqueElt); } - // Compute the re-sugared tuple type. - Type toSugarType = TupleType::get(toSugarFields, tc.Context); + // Convert each OpaqueValueExpr to the correct type. + SmallVector converted; + SmallVector labels; + SmallVector convertedElts; - // If we don't have to shuffle anything, we're done. - if (!anythingShuffled && fromTupleExpr) { - cs.setType(fromTupleExpr, toSugarType); + for (unsigned i = 0, e = sources.size(); i != e; ++i) { + unsigned source = sources[i]; + auto *fromElt = destructured[source]; - // Update the types of parentheses around the tuple expression. - rebuildIdentityExprs(cs, expr, toSugarType); + // Actually convert the source element. + auto toEltType = toTuple->getElementType(i); + auto toLabel = toTuple->getElement(i).getName(); - return expr; + auto *toElt + = coerceToType(fromElt, toEltType, + locator.withPathElement( + LocatorPathElt::getTupleElement(source))); + if (!toElt) + return nullptr; + + converted.push_back(toElt); + labels.push_back(toLabel); + convertedElts.emplace_back(toEltType, toLabel, ParameterTypeFlags()); } - // Create the tuple shuffle. + // Create the result tuple, written in terms of the destructured + // OpaqueValueExprs. + auto *result = TupleExpr::createImplicit(tc.Context, converted, labels); + result->setType(TupleType::get(convertedElts, tc.Context)); + cs.cacheType(result); + + // Create the tuple conversion. return - cs.cacheType(TupleShuffleExpr::create(tc.Context, - expr, sources, - toSugarType)); + cs.cacheType(DestructureTupleExpr::create(tc.Context, + destructured, expr, result, + toTuple)); } static Type getMetatypeSuperclass(Type t, TypeChecker &tc) { diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 4685e590049c0..c766e02130b8f 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -1860,8 +1860,8 @@ static const Expr *lookThroughExprsToImmediateDeallocation(const Expr *E) { while (true) { E = E->getValueProvidingExpr(); - // We don't currently deal with tuple shuffles. - if (isa(E)) + // We don't currently deal with tuple destructuring. + if (isa(E)) return E; // If we have a TupleElementExpr with a child TupleExpr, dig into that diff --git a/test/Constraints/members.swift b/test/Constraints/members.swift index 368fa87749072..25255a3f51e23 100644 --- a/test/Constraints/members.swift +++ b/test/Constraints/members.swift @@ -558,7 +558,6 @@ func rdar_48114578() { func foo(_ a: [String], _ b: Int) -> S { let v = (a, b) return .valueOf(v) - // expected-error@-1 {{cannot express tuple conversion '([String], Int)' to '(a: [String]?, b: Int)'}} } func bar(_ a: [String], _ b: Int) -> S { diff --git a/test/Constraints/protocols.swift b/test/Constraints/protocols.swift index e0a070bb9e1ca..a538d97a72a71 100644 --- a/test/Constraints/protocols.swift +++ b/test/Constraints/protocols.swift @@ -339,9 +339,7 @@ func testClonableArchetype(_ t: T) { func testClonableExistential(_ v: Clonable, _ vv: Clonable.Type) { let _: Clonable? = v.maybeClone() let _: Clonable?? = v.doubleMaybeClone() - // FIXME: Tuple-to-tuple conversions are not implemented let _: (Clonable, Clonable) = v.subdivideClone() - // expected-error@-1{{cannot express tuple conversion '(Clonable, Clonable)' to '(Clonable, Clonable)'}} let _: Clonable.Type = v.metatypeOfClone() let _: () -> Clonable = v.goodClonerFn() diff --git a/test/Constraints/tuple.swift b/test/Constraints/tuple.swift index e3db589aa4ce5..7845d70d491d9 100644 --- a/test/Constraints/tuple.swift +++ b/test/Constraints/tuple.swift @@ -28,6 +28,35 @@ func f6(_: (i: Int, j: Int), k: Int = 15) {} // Conversions and shuffles //===----------------------------------------------------------------------===// +func foo(a : [(some: Int, (key: Int, value: String))]) -> String { + for (i , (j, k)) in a { + if i == j { return k } + } +} + +func rdar28207648() -> [(Int, CustomStringConvertible)] { + let v : [(Int, Int)] = [] + return v as [(Int, CustomStringConvertible)] +} + +class rdar28207648Base {} +class rdar28207648Derived : rdar28207648Base {} + +func rdar28207648(x: (Int, rdar28207648Derived)) -> (Int, rdar28207648Base) { + return x as (Int, rdar28207648Base) +} + +public typealias Success = (response: T, data: V?) + +public enum Result { + case success(Success) + case error(Error) +} + + +let a = Success(response: 3, data: 3) +let success: Result = .success(a) + // Variadic functions. f4() f4(1) diff --git a/test/SILGen/tuples.swift b/test/SILGen/tuples.swift index 1f5caa5c5b34b..630842fb164fd 100644 --- a/test/SILGen/tuples.swift +++ b/test/SILGen/tuples.swift @@ -1,8 +1,8 @@ // RUN: %target-swift-emit-silgen -module-name tuples %s | %FileCheck %s -class C {} +public class C {} -enum Foo { +public enum Foo { case X(C, Int) } @@ -15,8 +15,8 @@ func matchFoo(x x: Foo) { } } -protocol P { func foo() } -struct A : P { func foo() {} } +public protocol P { func foo() } +public struct A : P { public func foo() {} } func make_int() -> Int { return 0 } func make_p() -> P { return A() } @@ -28,33 +28,39 @@ func testShuffleOpaque() { // CHECK-NEXT: [[PBX:%.*]] = project_box [[X]] // CHECK: [[Y:%.*]] = alloc_box ${ var Int } // CHECK-NEXT: [[PBY:%.*]] = project_box [[Y]] + // CHECK-NEXT: [[TMP:%.*]] = alloc_stack $P // CHECK: [[T0:%.*]] = function_ref @$s6tuples7make_xySi1x_AA1P_p1ytyF - // CHECK-NEXT: [[T1:%.*]] = apply [[T0]]([[PBX]]) + // CHECK-NEXT: [[T1:%.*]] = apply [[T0]]([[TMP]]) + // CHECK-NEXT: copy_addr [take] [[TMP]] to [initialization] [[PBX]] : $*P // CHECK-NEXT: store [[T1]] to [trivial] [[PBY]] + // CHECK-NEXT: dealloc_stack [[TMP]] var (x,y) : (y:P, x:Int) = make_xy() // CHECK-NEXT: [[PAIR:%.*]] = alloc_box ${ var (y: P, x: Int) } // CHECK-NEXT: [[PBPAIR:%.*]] = project_box [[PAIR]] - // CHECK-NEXT: [[PAIR_0:%.*]] = tuple_element_addr [[PBPAIR]] : $*(y: P, x: Int), 0 - // CHECK-NEXT: [[PAIR_1:%.*]] = tuple_element_addr [[PBPAIR]] : $*(y: P, x: Int), 1 + // CHECK-NEXT: [[TMP:%.*]] = alloc_stack $P // CHECK-NEXT: // function_ref // CHECK-NEXT: [[T0:%.*]] = function_ref @$s6tuples7make_xySi1x_AA1P_p1ytyF - // CHECK-NEXT: [[T1:%.*]] = apply [[T0]]([[PAIR_0]]) + // CHECK-NEXT: [[T1:%.*]] = apply [[T0]]([[TMP]]) + // CHECK-NEXT: [[PAIR_0:%.*]] = tuple_element_addr [[PBPAIR]] : $*(y: P, x: Int), 0 + // CHECK-NEXT: [[PAIR_1:%.*]] = tuple_element_addr [[PBPAIR]] : $*(y: P, x: Int), 1 + // CHECK-NEXT: copy_addr [take] [[TMP]] to [initialization] [[PAIR_0]] : $*P // CHECK-NEXT: store [[T1]] to [trivial] [[PAIR_1]] + // CHECK-NEXT: dealloc_stack [[TMP]] var pair : (y:P, x:Int) = make_xy() - // CHECK-NEXT: [[TEMP:%.*]] = alloc_stack $P + // CHECK-NEXT: [[TMP:%.*]] = alloc_stack $P // CHECK-NEXT: // function_ref // CHECK-NEXT: [[T0:%.*]] = function_ref @$s6tuples7make_xySi1x_AA1P_p1ytyF - // CHECK-NEXT: [[T1:%.*]] = apply [[T0]]([[TEMP]]) + // CHECK-NEXT: [[T1:%.*]] = apply [[T0]]([[TMP]]) // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [unknown] [[PBPAIR]] : $*(y: P, x: Int) // CHECK-NEXT: [[PAIR_0:%.*]] = tuple_element_addr [[WRITE]] : $*(y: P, x: Int), 0 - // CHECK-NEXT: copy_addr [take] [[TEMP]] to [[PAIR_0]] + // CHECK-NEXT: copy_addr [take] [[TMP]] to [[PAIR_0]] // CHECK-NEXT: [[PAIR_1:%.*]] = tuple_element_addr [[WRITE]] : $*(y: P, x: Int), 1 // CHECK-NEXT: assign [[T1]] to [[PAIR_1]] // CHECK-NEXT: end_access [[WRITE]] : $*(y: P, x: Int) - // CHECK-NEXT: dealloc_stack [[TEMP]] + // CHECK-NEXT: dealloc_stack [[TMP]] pair = make_xy() } @@ -64,32 +70,36 @@ func testShuffleTuple() { // CHECK-NEXT: [[PBX:%.*]] = project_box [[X]] // CHECK: [[Y:%.*]] = alloc_box ${ var Int } // CHECK-NEXT: [[PBY:%.*]] = project_box [[Y]] - - // CHECK: [[T0:%.*]] = function_ref @$s6tuples8make_intSiyF + // CHECK-NEXT: // function_ref + // CHECK-NEXT: [[T0:%.*]] = function_ref @$s6tuples8make_intSiyF // CHECK-NEXT: [[T1:%.*]] = apply [[T0]]() - // CHECK-NEXT: store [[T1]] to [trivial] [[PBY]] + // CHECK-NEXT: [[TMP:%.*]] = alloc_stack $P // CHECK-NEXT: // function_ref // CHECK-NEXT: [[T0:%.*]] = function_ref @$s6tuples6make_pAA1P_pyF - // CHECK-NEXT: apply [[T0]]([[PBX]]) + // CHECK-NEXT: apply [[T0]]([[TMP]]) + // CHECK-NEXT: copy_addr [take] [[TMP]] to [initialization] [[PBX]] + // CHECK-NEXT: store [[T1]] to [trivial] [[PBY]] + // CHECK-NEXT: dealloc_stack [[TMP]] var (x,y) : (y:P, x:Int) = (x: make_int(), y: make_p()) // CHECK-NEXT: [[PAIR:%.*]] = alloc_box ${ var (y: P, x: Int) } // CHECK-NEXT: [[PBPAIR:%.*]] = project_box [[PAIR]] - // CHECK-NEXT: [[PAIR_0:%.*]] = tuple_element_addr [[PBPAIR]] : $*(y: P, x: Int), 0 - // CHECK-NEXT: [[PAIR_1:%.*]] = tuple_element_addr [[PBPAIR]] : $*(y: P, x: Int), 1 // CHECK-NEXT: // function_ref - // CHECK: [[T0:%.*]] = function_ref @$s6tuples8make_intSiyF + // CHECK-NEXT: [[T0:%.*]] = function_ref @$s6tuples8make_intSiyF // CHECK-NEXT: [[T1:%.*]] = apply [[T0]]() - // CHECK-NEXT: store [[T1]] to [trivial] [[PAIR_1]] + // CHECK-NEXT: [[TMP:%.*]] = alloc_stack $P // CHECK-NEXT: // function_ref // CHECK-NEXT: [[T0:%.*]] = function_ref @$s6tuples6make_pAA1P_pyF - // CHECK-NEXT: apply [[T0]]([[PAIR_0]]) + // CHECK-NEXT: apply [[T0]]([[TMP]]) + // CHECK-NEXT: [[PAIR_0:%.*]] = tuple_element_addr [[PBPAIR]] : $*(y: P, x: Int), 0 + // CHECK-NEXT: [[PAIR_1:%.*]] = tuple_element_addr [[PBPAIR]] : $*(y: P, x: Int), 1 + // CHECK-NEXT: copy_addr [take] [[TMP]] to [initialization] [[PBX]] + // CHECK-NEXT: store [[T1]] to [trivial] [[PAIR_1]] + // CHECK-NEXT: dealloc_stack [[TMP]] var pair : (y:P, x:Int) = (x: make_int(), y: make_p()) - // This isn't really optimal; we should be evaluating make_p directly - // into the temporary. // CHECK-NEXT: // function_ref - // CHECK: [[T0:%.*]] = function_ref @$s6tuples8make_intSiyF + // CHECK-NEXT: [[T0:%.*]] = function_ref @$s6tuples8make_intSiyF // CHECK-NEXT: [[INT:%.*]] = apply [[T0]]() // CHECK-NEXT: [[TEMP:%.*]] = alloc_stack $P // CHECK-NEXT: // function_ref @@ -131,7 +141,7 @@ func testTupleUnsplat() { // Make sure that we use a load_borrow instead of a load [take] when RValues are // formed with isGuaranteed set. extension P { - // CHECK-LABEL: sil hidden [ossa] @$s6tuples1PPAAE12immutableUse5tupleyAA1CC5index_x5valuet_tFZ + // CHECK-LABEL: sil [ossa] @$s6tuples1PPAAE12immutableUse5tupleyAA1CC5index_x5valuet_tFZ // CHECK: bb0([[TUP0:%.*]] : @guaranteed $C, [[TUP1:%.*]] : $*Self // Allocate space for the RValue. // CHECK: [[RVALUE:%.*]] = alloc_stack $(index: C, value: Self), let, name "tuple" @@ -176,3 +186,30 @@ extension P { public func testTupleAssign(x: inout [Int]) { (x[0], x[1]) = (0, 1) } + +// CHECK-LABEL: sil [ossa] @$s6tuples16testTupleSubtype1x1y1zyAA1CC_SiSStF : $@convention(thin) (@guaranteed C, Int, @guaranteed String) -> () { +// CHECK: [[X:%.*]] = copy_value %0 : $C +// CHECK: [[Z:%.*]] = copy_value %2 : $String +// CHECK: [[INPUT:%.*]] = tuple $(x: C, y: Int, z: String) ([[X]], %1, [[Z]]) +// CHECK: [[OUTPUT:%.*]] = alloc_stack $(y: Optional, z: Any, x: AnyObject) +// CHECK: [[INPUT_BORROW:%.*]] = begin_borrow [[INPUT]] : $(x: C, y: Int, z: String) +// CHECK: [[INPUT_COPY:%.*]] = copy_value [[INPUT_BORROW]] : $(x: C, y: Int, z: String) +// CHECK: ([[X:%.*]], [[Y:%.*]], [[Z:%.*]]) = destructure_tuple %12 : $(x: C, y: Int, z: String) +// CHECK: [[Y_ADDR:%.*]] = tuple_element_addr %10 : $*(y: Optional, z: Any, x: AnyObject), 0 +// CHECK: [[Z_ADDR:%.*]] = tuple_element_addr %10 : $*(y: Optional, z: Any, x: AnyObject), 1 +// CHECK: [[X_ADDR:%.*]] = tuple_element_addr %10 : $*(y: Optional, z: Any, x: AnyObject), 2 +// CHECK: [[NEW_Y:%.*]] = enum $Optional, #Optional.some!enumelt.1, %14 : $Int +// CHECK: store [[NEW_Y]] to [trivial] [[Y_ADDR]] : $*Optional +// CHECK: [[NEW_Z:%.*]] = init_existential_addr [[Z_ADDR]] : $*Any, $String +// CHECK: store [[Z]] to [init] [[NEW_Z]] : $*String +// CHECK: [[NEW_X:%.*]] = init_existential_ref [[X]] : $C : $C, $AnyObject +// CHECK: store [[NEW_X]] to [init] [[X_ADDR]] : $*AnyObject +// CHECK: end_borrow [[INPUT_BORROW]] : $(x: C, y: Int, z: String) +// CHECK: destroy_addr [[OUTPUT]] : $*(y: Optional, z: Any, x: AnyObject) +// CHECK: dealloc_stack [[OUTPUT]] : $*(y: Optional, z: Any, x: AnyObject) +// CHECK: destroy_value [[INPUT]] : $(x: C, y: Int, z: String) + +public func testTupleSubtype(x: C, y: Int, z: String) { + let input = (x: x, y: y, z: z) + let output: (y: Int?, z: Any, x: AnyObject) = input +} \ No newline at end of file diff --git a/test/Sema/diag_express_tuple.swift b/test/Sema/diag_express_tuple.swift deleted file mode 100644 index 7253e1b5d5ff5..0000000000000 --- a/test/Sema/diag_express_tuple.swift +++ /dev/null @@ -1,19 +0,0 @@ -// RUN: %target-typecheck-verify-swift - -func foo(a : [(some: Int, (key: Int, value: String))]) -> String { - for (i , (j, k)) in a { // expected-error {{cannot express tuple conversion '(some: Int, (key: Int, value: String))' to '(Int, (Int, String))'}}{8-8=some: }} {{13-13=key: }} {{16-16=value: }} - if i == j { return k } - } -} - -func rdar28207648() -> [(Int, CustomStringConvertible)] { - let v : [(Int, Int)] = [] - return v as [(Int, CustomStringConvertible)] // expected-error {{cannot express tuple conversion '(Int, Int)' to '(Int, CustomStringConvertible)'}} -} - -class rdar28207648Base {} -class rdar28207648Derived : rdar28207648Base {} - -func rdar28207648(x: (Int, rdar28207648Derived)) -> (Int, rdar28207648Base) { - return x as (Int, rdar28207648Base) // expected-error {{cannot express tuple conversion '(Int, rdar28207648Derived)' to '(Int, rdar28207648Base)'}} -}