diff --git a/docs/SIL.rst b/docs/SIL.rst index 3bb31eb6714e9..893b4db6d8856 100644 --- a/docs/SIL.rst +++ b/docs/SIL.rst @@ -2333,6 +2333,32 @@ with a sequence that also correctly destroys the current value. This instruction is only valid in Raw SIL and is rewritten as appropriate by the definitive initialization pass. +assign_by_delegate +`````````````````` +:: + + sil-instruction ::= 'assign_by_delegate' sil-operand 'to' sil-operand ',' 'init' sil-operand ',' 'set' sil-operand + + assign_by_delegate %0 : $S to %1 : $*T, init %2 : $F, set %3 : $G + // $S can be a value or address type + // $T must be the type of a property delegate. + // $F must be a function type, taking $S as a single argument and returning $T + // $G must be a function type, taking $S as a single argument and with not return value + +Similar to the ``assign`` instruction, but the assignment is done via a +delegate. + +In case of an initialization, the function ``%2`` is called with ``%0`` as +argument. The result is stored to ``%1``. In case ``%2`` is an address type, +it is simply passed as a first out-argument to ``%2``. + +In case of a re-assignment, the function ``%3`` is called with ``%0`` as +argument. As ``%3`` is a setter (e.g. for the property in the containing +nominal type), the destination address ``%1`` is not used in this case. + +This instruction is only valid in Raw SIL and is rewritten as appropriate +by the definitive initialization pass. + mark_uninitialized `````````````````` :: diff --git a/include/swift/SIL/InstructionUtils.h b/include/swift/SIL/InstructionUtils.h index 88dd842db2973..cb307bdc63f8f 100644 --- a/include/swift/SIL/InstructionUtils.h +++ b/include/swift/SIL/InstructionUtils.h @@ -134,6 +134,10 @@ bool isSanitizerInstrumentation(SILInstruction *Instruction); /// argument of the partial apply if it is. SILValue isPartialApplyOfReabstractionThunk(PartialApplyInst *PAI); +/// Returns true if \p PAI is only used by an assign_by_delegate instruction as +/// init or set function. +bool onlyUsedByAssignByDelegate(PartialApplyInst *PAI); + /// If V is a function closure, return the reaching set of partial_apply's. void findClosuresForFunctionValue(SILValue V, TinyPtrVector &results); diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index a2790eabd60b6..a8448a8dada52 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -842,6 +842,16 @@ class SILBuilder { Qualifier)); } + AssignByDelegateInst *createAssignByDelegate(SILLocation Loc, + SILValue Src, SILValue Dest, + SILValue Initializer, + SILValue Setter, + AssignOwnershipQualifier Qualifier) { + return insert(new (getModule()) + AssignByDelegateInst(getSILDebugLocation(Loc), Src, Dest, + Initializer, Setter, Qualifier)); + } + StoreBorrowInst *createStoreBorrow(SILLocation Loc, SILValue Src, SILValue DestAddr) { return insert(new (getModule()) diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 6c6eafa76d969..2d61ebe4e661b 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -1204,6 +1204,18 @@ void SILCloner::visitAssignInst(AssignInst *Inst) { Inst->getOwnershipQualifier())); } +template +void SILCloner::visitAssignByDelegateInst(AssignByDelegateInst *Inst) { + getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); + recordClonedInstruction( + Inst, getBuilder().createAssignByDelegate(getOpLocation(Inst->getLoc()), + getOpValue(Inst->getSrc()), + getOpValue(Inst->getDest()), + getOpValue(Inst->getInitializer()), + getOpValue(Inst->getSetter()), + Inst->getOwnershipQualifier())); +} + template void SILCloner::visitMarkUninitializedInst(MarkUninitializedInst *Inst) { diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 4d7b7a82da620..a181d7ca362a1 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -3708,19 +3708,17 @@ enum class AssignOwnershipQualifier { }; static_assert(2 == SILNode::NumAssignOwnershipQualifierBits, "Size mismatch"); -/// AssignInst - Represents an abstract assignment to a memory location, which -/// may either be an initialization or a store sequence. This is only valid in -/// Raw SIL. -class AssignInst - : public InstructionBase { - friend SILBuilder; +template +class AssignInstBase + : public InstructionBase { - FixedOperandList<2> Operands; +protected: + FixedOperandList Operands; - AssignInst(SILDebugLocation DebugLoc, SILValue Src, SILValue Dest, - AssignOwnershipQualifier Qualifier = - AssignOwnershipQualifier::Unknown); + template + AssignInstBase(SILDebugLocation DebugLoc, T&&...args) : + InstructionBase(DebugLoc), + Operands(this, std::forward(args)...) { } public: enum { @@ -3735,7 +3733,20 @@ class AssignInst ArrayRef getAllOperands() const { return Operands.asArray(); } MutableArrayRef getAllOperands() { return Operands.asArray(); } +}; + +/// AssignInst - Represents an abstract assignment to a memory location, which +/// may either be an initialization or a store sequence. This is only valid in +/// Raw SIL. +class AssignInst + : public AssignInstBase { + friend SILBuilder; + AssignInst(SILDebugLocation DebugLoc, SILValue Src, SILValue Dest, + AssignOwnershipQualifier Qualifier = + AssignOwnershipQualifier::Unknown); + +public: AssignOwnershipQualifier getOwnershipQualifier() const { return AssignOwnershipQualifier( SILInstruction::Bits.AssignInst.OwnershipQualifier); @@ -3745,6 +3756,32 @@ class AssignInst } }; +/// AssignByDelegateInst - Represents an abstract assignment via a delegate, +/// which may either be an initialization or a store sequence. This is only +/// valid in Raw SIL. +class AssignByDelegateInst + : public AssignInstBase { + friend SILBuilder; + + AssignByDelegateInst(SILDebugLocation DebugLoc, SILValue Src, SILValue Dest, + SILValue Initializer, SILValue Setter, + AssignOwnershipQualifier Qualifier = + AssignOwnershipQualifier::Unknown); + +public: + + SILValue getInitializer() { return Operands[2].get(); } + SILValue getSetter() { return Operands[3].get(); } + + AssignOwnershipQualifier getOwnershipQualifier() const { + return AssignOwnershipQualifier( + SILInstruction::Bits.AssignByDelegateInst.OwnershipQualifier); + } + void setOwnershipQualifier(AssignOwnershipQualifier qualifier) { + SILInstruction::Bits.AssignByDelegateInst.OwnershipQualifier = unsigned(qualifier); + } +}; + /// Indicates that a memory location is uninitialized at this point and needs to /// be initialized by the end of the function and before any escape point for /// this instruction. This is only valid in Raw SIL. diff --git a/include/swift/SIL/SILNode.h b/include/swift/SIL/SILNode.h index d582c4b3901ab..8a6f546f5b6e7 100644 --- a/include/swift/SIL/SILNode.h +++ b/include/swift/SIL/SILNode.h @@ -285,6 +285,10 @@ class alignas(8) SILNode { NumAssignOwnershipQualifierBits, OwnershipQualifier : NumAssignOwnershipQualifierBits ); + SWIFT_INLINE_BITFIELD(AssignByDelegateInst, NonValueInstruction, + NumAssignOwnershipQualifierBits, + OwnershipQualifier : NumAssignOwnershipQualifierBits + ); SWIFT_INLINE_BITFIELD(UncheckedOwnershipConversionInst,SingleValueInstruction, NumVOKindBits, diff --git a/include/swift/SIL/SILNodes.def b/include/swift/SIL/SILNodes.def index d90c2d1e85c4c..783235390098f 100644 --- a/include/swift/SIL/SILNodes.def +++ b/include/swift/SIL/SILNodes.def @@ -805,6 +805,8 @@ NON_VALUE_INST(StoreInst, store, SILInstruction, MayWrite, DoesNotRelease) NON_VALUE_INST(AssignInst, assign, SILInstruction, MayWrite, DoesNotRelease) +NON_VALUE_INST(AssignByDelegateInst, assign_by_delegate, + SILInstruction, MayWrite, DoesNotRelease) NON_VALUE_INST(MarkFunctionEscapeInst, mark_function_escape, SILInstruction, None, DoesNotRelease) NON_VALUE_INST(DebugValueInst, debug_value, diff --git a/include/swift/Serialization/ModuleFormat.h b/include/swift/Serialization/ModuleFormat.h index c9f1256bf803c..a5c82844fa1ac 100644 --- a/include/swift/Serialization/ModuleFormat.h +++ b/include/swift/Serialization/ModuleFormat.h @@ -52,7 +52,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 480; // property delegates +const uint16_t SWIFTMODULE_VERSION_MINOR = 481; // assign_by_delegate using DeclIDField = BCFixed<31>; diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 4a8b21691eb67..8649f413c4ac5 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -933,6 +933,9 @@ class IRGenSILFunction : void visitAssignInst(AssignInst *i) { llvm_unreachable("assign is not valid in canonical SIL"); } + void visitAssignByDelegateInst(AssignByDelegateInst *i) { + llvm_unreachable("assign_by_delegate is not valid in canonical SIL"); + } void visitMarkUninitializedInst(MarkUninitializedInst *i) { llvm_unreachable("mark_uninitialized is not valid in canonical SIL"); } diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index c5226b31b296c..48d9286272193 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -3540,6 +3540,34 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { break; } + case SILInstructionKind::AssignByDelegateInst: { + SILValue Src, DestAddr, InitFn, SetFn; + SourceLoc DestLoc; + AssignOwnershipQualifier AssignQualifier; + if (parseTypedValueRef(Src, B) || + parseVerbatim("to") || + parseAssignOwnershipQualifier(AssignQualifier, *this) || + parseTypedValueRef(DestAddr, DestLoc, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseVerbatim("init") || + parseTypedValueRef(InitFn, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseVerbatim("set") || + parseTypedValueRef(SetFn, B) || + parseSILDebugLocation(InstLoc, B)) + return true; + + if (!DestAddr->getType().isAddress()) { + P.diagnose(DestLoc, diag::sil_operand_not_address, "destination", + OpcodeName); + return true; + } + + ResultVal = B.createAssignByDelegate(InstLoc, Src, DestAddr, InitFn, SetFn, + AssignQualifier); + break; + } + case SILInstructionKind::BeginAccessInst: case SILInstructionKind::BeginUnpairedAccessInst: case SILInstructionKind::EndAccessInst: diff --git a/lib/SIL/InstructionUtils.cpp b/lib/SIL/InstructionUtils.cpp index a9ffe945647a8..f23da199ec1fb 100644 --- a/lib/SIL/InstructionUtils.cpp +++ b/lib/SIL/InstructionUtils.cpp @@ -374,6 +374,21 @@ SILValue swift::isPartialApplyOfReabstractionThunk(PartialApplyInst *PAI) { return Arg; } +bool swift::onlyUsedByAssignByDelegate(PartialApplyInst *PAI) { + bool usedByAssignByDelegate = false; + for (Operand *Op : PAI->getUses()) { + SILInstruction *User = Op->getUser(); + if (isa(User) && Op->getOperandNumber() >= 2) { + usedByAssignByDelegate = true; + continue; + } + if (isa(User)) + continue; + return false; + } + return usedByAssignByDelegate; +} + /// Given a block used as a noescape function argument, attempt to find all /// Swift closures that invoking the block will call. The StoredClosures may not /// actually be partial_apply instructions. They may be copied, block arguments, diff --git a/lib/SIL/MemAccessUtils.cpp b/lib/SIL/MemAccessUtils.cpp index 7fdb51295cc23..28c540fc98163 100644 --- a/lib/SIL/MemAccessUtils.cpp +++ b/lib/SIL/MemAccessUtils.cpp @@ -678,6 +678,7 @@ void swift::visitAccessedAddress(SILInstruction *I, llvm_unreachable("unexpected memory access."); case SILInstructionKind::AssignInst: + case SILInstructionKind::AssignByDelegateInst: visitor(&I->getAllOperands()[AssignInst::Dest]); return; diff --git a/lib/SIL/OperandOwnership.cpp b/lib/SIL/OperandOwnership.cpp index b0ac2139ca1f7..e6694dbd3ac67 100644 --- a/lib/SIL/OperandOwnership.cpp +++ b/lib/SIL/OperandOwnership.cpp @@ -840,6 +840,16 @@ OperandOwnershipKindClassifier::visitAssignInst(AssignInst *i) { UseLifetimeConstraint::MustBeInvalidated); } +OperandOwnershipKindMap +OperandOwnershipKindClassifier::visitAssignByDelegateInst(AssignByDelegateInst *i) { + if (getValue() != i->getSrc()) { + return Map::allLive(); + } + + return Map::compatibilityMap(ValueOwnershipKind::Owned, + UseLifetimeConstraint::MustBeInvalidated); +} + OperandOwnershipKindMap OperandOwnershipKindClassifier::visitStoreInst(StoreInst *i) { if (getValue() != i->getSrc()) { diff --git a/lib/SIL/SILInstructions.cpp b/lib/SIL/SILInstructions.cpp index 8df99bb2b6764..cda5c617f7a3b 100644 --- a/lib/SIL/SILInstructions.cpp +++ b/lib/SIL/SILInstructions.cpp @@ -813,11 +813,22 @@ StringRef swift::getSILAccessEnforcementName(SILAccessEnforcement enforcement) { } AssignInst::AssignInst(SILDebugLocation Loc, SILValue Src, SILValue Dest, - AssignOwnershipQualifier Qualifier) - : InstructionBase(Loc), Operands(this, Src, Dest) { + AssignOwnershipQualifier Qualifier) : + AssignInstBase(Loc, Src, Dest) { SILInstruction::Bits.AssignInst.OwnershipQualifier = unsigned(Qualifier); } +AssignByDelegateInst::AssignByDelegateInst(SILDebugLocation Loc, + SILValue Src, SILValue Dest, + SILValue Initializer, + SILValue Setter, + AssignOwnershipQualifier Qualifier) : + AssignInstBase(Loc, Src, Dest, Initializer, Setter) { + assert(Initializer->getType().is()); + SILInstruction::Bits.AssignByDelegateInst.OwnershipQualifier = + unsigned(Qualifier); +} + MarkFunctionEscapeInst * MarkFunctionEscapeInst::create(SILDebugLocation Loc, ArrayRef Elements, SILFunction &F) { diff --git a/lib/SIL/SILPrinter.cpp b/lib/SIL/SILPrinter.cpp index c353d48f44f8c..85c84369a1d0a 100644 --- a/lib/SIL/SILPrinter.cpp +++ b/lib/SIL/SILPrinter.cpp @@ -1309,6 +1309,14 @@ class SILPrinter : public SILInstructionVisitor { *this << getIDAndType(AI->getDest()); } + void visitAssignByDelegateInst(AssignByDelegateInst *AI) { + *this << getIDAndType(AI->getSrc()) << " to "; + printAssignOwnershipQualifier(AI->getOwnershipQualifier()); + *this << getIDAndType(AI->getDest()) + << ", init " << getIDAndType(AI->getInitializer()) + << ", set " << getIDAndType(AI->getSetter()); + } + void visitMarkUninitializedInst(MarkUninitializedInst *MU) { switch (MU->getKind()) { case MarkUninitializedInst::Var: *this << "[var] "; break; diff --git a/lib/SIL/SILVerifier.cpp b/lib/SIL/SILVerifier.cpp index cd18ab46ad9c0..46b35d057af38 100644 --- a/lib/SIL/SILVerifier.cpp +++ b/lib/SIL/SILVerifier.cpp @@ -1757,6 +1757,53 @@ class SILVerifier : public SILVerifierBase { "Store operand type and dest type mismatch"); } + void checkAssignByDelegateInst(AssignByDelegateInst *AI) { + SILValue Src = AI->getSrc(), Dest = AI->getDest(); + require(AI->getModule().getStage() == SILStage::Raw, + "assign instruction can only exist in raw SIL"); + require(Dest->getType().isAddress(), "Must store to an address dest"); + + unsigned indirectInitResults = Src->getType().isAddress() ? 1 : 0; + + SILValue initFn = AI->getInitializer(); + CanSILFunctionType initTy = initFn->getType().castTo(); + SILFunctionConventions initConv(initTy, AI->getModule()); + require(initConv.getNumIndirectSILResults() == indirectInitResults, + "init function has wrong number of indirect results"); + unsigned firstArgIdx = initConv.getSILArgIndexOfFirstParam(); + require(initConv.getNumSILArguments() == firstArgIdx + 1, + "init function has wrong number of arguments"); + require(Src->getType() == initConv.getSILArgumentType(firstArgIdx), + "wrong argument type of init function"); + switch (initConv.getNumIndirectSILResults()) { + case 0: + require(initConv.getNumDirectSILResults() == 1, + "wrong number of init function results"); + require(Dest->getType().getObjectType() == + initConv.getDirectSILResultTypes().front(), + "wrong init function result type"); + break; + case 1: + require(initConv.getNumDirectSILResults() == 0, + "wrong number of init function results"); + require(Dest->getType() == initConv.getIndirectSILResultTypes().front(), + "wrong indirect init function result type"); + break; + default: + require(false, "wrong number of indirect init function results"); + } + + SILValue setterFn = AI->getSetter(); + CanSILFunctionType setterTy = setterFn->getType().castTo(); + SILFunctionConventions setterConv(setterTy, AI->getModule()); + require(setterConv.getNumIndirectSILResults() == 0, + "set function has indirect results"); + require(setterConv.getNumSILArguments() == 1, + "init function has wrong number of arguments"); + require(Src->getType() == setterConv.getSILArgumentType(0), + "wrong argument type of init function"); + } + #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \ void checkLoad##Name##Inst(Load##Name##Inst *LWI) { \ require(LWI->getType().isObject(), "Result of load must be an object"); \ diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 669e56c83e98c..f88fa958d00bf 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -5539,6 +5539,29 @@ RValue SILGenFunction::emitApplyAllocatingInitializer(SILLocation loc, return result; } +RValue SILGenFunction::emitApplyPropertyDelegateAllocator(SILLocation loc, + SubstitutionMap subs, + SILDeclRef ctorRef, + Type delegateTy, + CanAnyFunctionType funcTy) { + Callee callee = Callee::forDirect(*this, ctorRef, subs, loc); + + MetatypeType *MTty = MetatypeType::get(delegateTy); + auto metatypeVal = B.createMetatype(loc, getLoweredType(MTty)); + ManagedValue mtManagedVal = ManagedValue::forUnmanaged(metatypeVal); + RValue metatypeRVal(*this, loc, MTty->getCanonicalType(), mtManagedVal); + + ArgumentSource ArgSrc(loc, std::move(metatypeRVal)); + FormalEvaluationScope writebacks(*this); + CallEmission emission(*this, std::move(callee), std::move(writebacks)); + CallSite callSite(loc, std::move(ArgSrc), false, funcTy); + + emission.addCallSite(std::move(callSite)); + + RValue RV = emission.apply(); + return RV; +} + /// Emit a literal that applies the various initializers. RValue SILGenFunction::emitLiteral(LiteralExpr *literal, SGFContext C) { ConcreteDeclRef builtinInit; diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 2948e21f1a513..ba1405d9ce805 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -1475,6 +1475,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction RValue &&args, Type overriddenSelfType, SGFContext ctx); + RValue emitApplyPropertyDelegateAllocator(SILLocation loc, + SubstitutionMap subs, + SILDeclRef ctorRef, + Type delegateTy, + CanAnyFunctionType funcTy); + CleanupHandle emitBeginApply(SILLocation loc, ManagedValue fn, SubstitutionMap subs, ArrayRef args, CanSILFunctionType substFnType, diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 022457f7e6d6c..e29f598f597e7 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -26,6 +26,7 @@ #include "swift/AST/DiagnosticsCommon.h" #include "swift/AST/DiagnosticsSIL.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/TypeCheckRequests.h" #include "swift/SIL/InstructionUtils.h" #include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/PrettyStackTrace.h" @@ -1291,6 +1292,12 @@ namespace { { } + bool hasPropertyDelegate() const { + if (auto *VD = dyn_cast(Storage)) + return VD->hasPropertyDelegate(); + return false; + } + void emitAssignWithSetter(SILGenFunction &SGF, SILLocation loc, LValue &&dest, ArgumentSource &&value) { assert(getAccessorDecl()->isSetter()); @@ -1344,6 +1351,86 @@ namespace { assert(getAccessorDecl()->isSetter()); SILDeclRef setter = Accessor; + if (hasPropertyDelegate() && IsOnSelfParameter) { + // This is delegated property. Instead of emitting a setter, emit an + // assign_by_delegate with the allocating initializer funcction and the + // setter function as arguments. DefiniteInitializtion will then decide + // between the two functions, depending if it's an initialization or a + // re-assignment. + // + VarDecl *field = dyn_cast(Storage); + VarDecl *backingVar = field->getPropertyDelegateBackingVar(); + assert(backingVar); + CanType ValType = backingVar->getType()->getCanonicalType(); + SILType varStorageType = + SGF.SGM.Types.getSubstitutedStorageType(backingVar, ValType); + auto typeData = + getLogicalStorageTypeData(SGF.SGM, getTypeData().AccessKind, ValType); + + // Get the address of the storage property. + ManagedValue proj; + if (BaseFormalType->mayHaveSuperclass()) { + RefElementComponent REC(backingVar, LValueOptions(), varStorageType, + typeData); + proj = std::move(REC).project(SGF, loc, base); + } else { + assert(BaseFormalType->getStructOrBoundGenericStruct()); + StructElementComponent SEC(backingVar, varStorageType, typeData); + proj = std::move(SEC).project(SGF, loc, base); + } + + // Create the allocating initializer function. It captures the metadata. + auto delegateInfo = getAttachedPropertyDelegateInfo(field); + auto ctor = delegateInfo.initialValueInit; + SubstitutionMap subs = backingVar->getType()->getMemberSubstitutionMap( + SGF.getModule().getSwiftModule(), ctor); + + Type ity = ctor->getInterfaceType(); + AnyFunctionType *substIty = ity.subst(subs)->castTo(); + + auto initRef = SILDeclRef(ctor, SILDeclRef::Kind::Allocator) + .asForeign(requiresForeignEntryPoint(ctor)); + RValue initFuncRV = + SGF.emitApplyPropertyDelegateAllocator(loc, subs,initRef, + backingVar->getType(), + CanAnyFunctionType(substIty)); + ManagedValue initFn = std::move(initFuncRV).getAsSingleValue(SGF, loc); + + // Create the allocating setter function. It captures the base address. + auto setterInfo = SGF.getConstantInfo(setter); + SILValue setterFRef = SGF.emitGlobalFunctionRef(loc, setter, setterInfo); + auto setterSubs = SGF.getFunction().getForwardingSubstitutionMap(); + + SILType closureTy = SILGenBuilder::getPartialApplyResultType( + setterFRef->getType(), 1, SGF.SGM.M, setterSubs, + ParameterConvention::Direct_Guaranteed); + + CanSILFunctionType setterTy = setterFRef->getType().castTo(); + SILFunctionConventions setterConv(setterTy, SGF.SGM.M); + + SILValue capturedBase; + unsigned argIdx = setterConv.getNumSILArguments() - 1; + if (setterConv.getSILArgumentConvention(argIdx).isInoutConvention()) { + capturedBase = base.getValue(); + } else { + capturedBase = base.copy(SGF, loc).forward(SGF); + } + + PartialApplyInst *setterPAI = + SGF.B.createPartialApply(loc, setterFRef, setterFRef->getType(), + setterSubs, { capturedBase }, closureTy); + ManagedValue setterFn = SGF.emitManagedRValueWithCleanup(setterPAI); + + // Create the assign_by_delete with the allocator and setter. + assert(value.isRValue()); + ManagedValue Mval = std::move(value).asKnownRValue(SGF). + getAsSingleValue(SGF, loc); + SGF.B.createAssignByDelegate(loc, Mval.forward(SGF), proj.forward(SGF), + initFn.getValue(), setterFn.getValue(), + AssignOwnershipQualifier::Unknown); + return; + } + FormalEvaluationScope scope(SGF); // Pass in just the setter. auto args = @@ -4137,6 +4224,8 @@ static bool trySetterPeephole(SILGenFunction &SGF, SILLocation loc, } auto &setterComponent = static_cast(component); + if (setterComponent.hasPropertyDelegate()) + return false; setterComponent.emitAssignWithSetter(SGF, loc, std::move(dest), std::move(src)); return true;; diff --git a/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp b/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp index 9b356e186d136..877a68eeea832 100644 --- a/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp +++ b/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp @@ -36,6 +36,7 @@ #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILUndef.h" +#include "swift/SIL/InstructionUtils.h" #include "swift/SILOptimizer/Analysis/ClosureScope.h" #include "swift/SILOptimizer/Analysis/PostOrderAnalysis.h" #include "swift/SILOptimizer/PassManager/Transforms.h" @@ -255,7 +256,8 @@ static void checkUsesOfAccess(BeginAccessInst *access) { for (auto *use : access->getUses()) { auto user = use->getUser(); assert(!isa(user)); - assert(!isa(user)); + assert(!isa(user) || + onlyUsedByAssignByDelegate(cast(user))); } #endif } diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index 4f75d2497c893..f86629e50d75a 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -356,7 +356,12 @@ DIMemoryObjectInfo::getPathStringToElement(unsigned Element, getElementCountRec(Module, FieldType, false); if (Element < NumFieldElements) { Result += '.'; - Result += VD->getName().str(); + auto originalProperty = VD->getOriginalDelegatedProperty(); + if (originalProperty) { + Result += originalProperty->getName().str(); + } else { + Result += VD->getName().str(); + } getPathStringToElementRec(Module, FieldType, Element, Result); return VD; } @@ -415,9 +420,14 @@ bool DIMemoryObjectInfo::isElementLetProperty(unsigned Element) const { //===----------------------------------------------------------------------===// /// onlyTouchesTrivialElements - Return true if all of the accessed elements -/// have trivial type. +/// have trivial type and the access itself is a trivial instruction. bool DIMemoryUse::onlyTouchesTrivialElements( const DIMemoryObjectInfo &MI) const { + // assign_by_delegate calls functions to assign a value. This is not + // considered as trivial. + if (isa(Inst)) + return false; + auto *F = Inst->getFunction(); for (unsigned i = FirstElement, e = i + NumElements; i != e; ++i) { @@ -741,9 +751,12 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { #include "swift/AST/ReferenceStorage.def" // Stores *to* the allocation are writes. - if ((isa(User) || isa(User)) && + if ((isa(User) || isa(User) || + isa(User)) && Op->getOperandNumber() == 1) { if (PointeeType.is()) { + assert(!isa(User) && + "cannot assign a typle with assign_by_delegate"); UsesToScalarize.push_back(User); continue; } @@ -753,7 +766,7 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { DIUseKind Kind; if (InStructSubElement) Kind = DIUseKind::PartialStore; - else if (isa(User)) + else if (isa(User) || isa(User)) Kind = DIUseKind::InitOrAssign; else if (PointeeType.isTrivial(*User->getFunction())) Kind = DIUseKind::InitOrAssign; @@ -990,6 +1003,11 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { continue; } + if (auto *PAI = dyn_cast(User)) { + if (onlyUsedByAssignByDelegate(PAI)) + continue; + } + // Sanitizer instrumentation is not user visible, so it should not // count as a use and must not affect compile-time diagnostics. if (isSanitizerInstrumentation(User)) @@ -1466,8 +1484,11 @@ void ElementUseCollector::collectClassSelfUses( // If this is a partial application of self, then this is an escape point // for it. - if (isa(User)) + if (auto *PAI = dyn_cast(User)) { + if (onlyUsedByAssignByDelegate(PAI)) + continue; Kind = DIUseKind::Escape; + } trackUse(DIMemoryUse(User, Kind, 0, TheMemory.NumElements)); } diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h index a11f655a7fb05..cc9b8c56add95 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h @@ -267,7 +267,7 @@ struct DIMemoryUse { } /// onlyTouchesTrivialElements - Return true if all of the accessed elements - /// have trivial type. + /// have trivial type and the access itself is a trivial instruction. bool onlyTouchesTrivialElements(const DIMemoryObjectInfo &MemoryInfo) const; /// getElementBitmask - Return a bitmask with the touched tuple elements diff --git a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp index 5037766b7ab60..85203f9de0ec9 100644 --- a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp +++ b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp @@ -979,6 +979,8 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) { addr = copyAddr->getDest(); else if (auto *assign = dyn_cast(inst)) addr = assign->getDest(); + else if (auto *assign = dyn_cast(inst)) + addr = assign->getDest(); else return false; @@ -1823,7 +1825,7 @@ void LifetimeChecker::handleSelfInitUse(unsigned UseID) { assert(TheMemory.NumElements == 1 && "delegating inits have a single elt"); // Lower Assign instructions if needed. - if (isa(Use.Inst)) + if (isa(Use.Inst) || isa(Use.Inst)) NeedsUpdateForInitState.push_back(UseID); } else { // super.init also requires that all ivars are initialized before the @@ -1899,6 +1901,24 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) { return; } + if (auto *AI = dyn_cast(Inst)) { + // Remove this instruction from our data structures, since we will be + // removing it. + Use.Inst = nullptr; + NonLoadUses.erase(Inst); + + if (TheMemory.isClassInitSelf() && + Use.Kind == DIUseKind::SelfInit) { + assert(InitKind == IsInitialization); + AI->setOwnershipQualifier(AssignOwnershipQualifier::Reinit); + } else { + AI->setOwnershipQualifier((InitKind == IsInitialization + ? AssignOwnershipQualifier::Init + : AssignOwnershipQualifier::Reassign)); + } + + return; + } // Ignore non-stores for SelfInits. assert(isa(Inst) && "Unknown store instruction!"); diff --git a/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp index ab0a5979f4ff4..b1addde6cae7c 100644 --- a/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp +++ b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp @@ -91,12 +91,101 @@ static void lowerAssignInstruction(SILBuilderWithScope &b, AssignInst *inst) { inst->eraseFromParent(); } +static void lowerAssignByDelegateInstruction(SILBuilderWithScope &b, + AssignByDelegateInst *inst, + SmallVectorImpl &accessMarkers) { + LLVM_DEBUG(llvm::dbgs() << " *** Lowering [isInit=" + << unsigned(inst->getOwnershipQualifier()) + << "]: " << *inst << "\n"); + + ++numAssignRewritten; + + SILValue src = inst->getSrc(); + SILValue dest = inst->getDest(); + SILLocation loc = inst->getLoc(); + SILArgumentConvention srcConvention(SILArgumentConvention::Indirect_Inout); + + switch (inst->getOwnershipQualifier()) { + case AssignOwnershipQualifier::Init: { + SILValue initFn = inst->getInitializer(); + CanSILFunctionType fTy = initFn->getType().castTo(); + SILFunctionConventions convention(fTy, inst->getModule()); + if (convention.hasIndirectSILResults()) { + b.createApply(loc, initFn, { dest, src }, false); + srcConvention = convention.getSILArgumentConvention(1); + } else { + SILValue wrappedSrc = b.createApply(loc, initFn, { src }, false); + b.createTrivialStoreOr(loc, wrappedSrc, dest, StoreOwnershipQualifier::Init); + srcConvention = convention.getSILArgumentConvention(0); + } + // TODO: remove the unused setter function, which usually is a dead + // partial_apply. + break; + } + case AssignOwnershipQualifier::Unknown: + case AssignOwnershipQualifier::Reassign: { + SILValue setterFn = inst->getSetter(); + CanSILFunctionType fTy = setterFn->getType().castTo(); + SILFunctionConventions convention(fTy, inst->getModule()); + assert(!convention.hasIndirectSILResults()); + srcConvention = convention.getSILArgumentConvention(0); + b.createApply(loc, setterFn, { src }, false); + + // The destination address is not used. Remove it if it is a dead access + // marker. This is important, because also the setter function contains + // access marker. In case those markers are dynamic it would cause a + // nested access violation. + if (auto *BA = dyn_cast(dest)) + accessMarkers.push_back(BA); + // TODO: remove the unused init function, which usually is a dead + // partial_apply. + break; + } + case AssignOwnershipQualifier::Reinit: + llvm_unreachable("wrong qualifier for assign_by_delegate"); + } + switch (srcConvention) { + case SILArgumentConvention::Indirect_In_Guaranteed: + b.createDestroyAddr(loc, src); + break; + case SILArgumentConvention::Direct_Guaranteed: + b.createDestroyValue(loc, src); + break; + case SILArgumentConvention::Direct_Unowned: + case SILArgumentConvention::Indirect_In: + case SILArgumentConvention::Indirect_In_Constant: + case SILArgumentConvention::Direct_Owned: + break; + case SILArgumentConvention::Indirect_Inout: + case SILArgumentConvention::Indirect_InoutAliasable: + case SILArgumentConvention::Indirect_Out: + case SILArgumentConvention::Direct_Deallocating: + llvm_unreachable("wrong convention for setter/initializer src argument"); + } + inst->eraseFromParent(); +} + +static void deleteDeadAccessMarker(BeginAccessInst *BA) { + for (Operand *Op : BA->getUses()) { + SILInstruction *User = Op->getUser(); + if (!isa(User)) + return; + } + for (Operand *Op : BA->getUses()) { + Op->getUser()->eraseFromParent(); + } + BA->eraseFromParent(); +} + /// lowerRawSILOperations - There are a variety of raw-sil instructions like /// 'assign' that are only used by this pass. Now that definite initialization /// checking is done, remove them. static bool lowerRawSILOperations(SILFunction &fn) { bool changed = false; + for (auto &bb : fn) { + SmallVector accessMarkers; + auto i = bb.begin(), e = bb.end(); while (i != e) { SILInstruction *inst = &*i; @@ -123,6 +212,13 @@ static bool lowerRawSILOperations(SILFunction &fn) { continue; } + if (auto *ai = dyn_cast(inst)) { + SILBuilderWithScope b(ai); + lowerAssignByDelegateInstruction(b, ai, accessMarkers); + changed = true; + continue; + } + // mark_uninitialized just becomes a noop, resolving to its operand. if (auto *mui = dyn_cast(inst)) { mui->replaceAllUsesWith(mui->getOperand()); @@ -138,6 +234,9 @@ static bool lowerRawSILOperations(SILFunction &fn) { continue; } } + for (BeginAccessInst *BA : accessMarkers) { + deleteDeadAccessMarker(BA); + } } return changed; } diff --git a/lib/SILOptimizer/Utils/SILInliner.cpp b/lib/SILOptimizer/Utils/SILInliner.cpp index a91ac5a74fe41..bc618895bd945 100644 --- a/lib/SILOptimizer/Utils/SILInliner.cpp +++ b/lib/SILOptimizer/Utils/SILInliner.cpp @@ -731,6 +731,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::ValueMetatypeInst: case SILInstructionKind::WitnessMethodInst: case SILInstructionKind::AssignInst: + case SILInstructionKind::AssignByDelegateInst: case SILInstructionKind::BranchInst: case SILInstructionKind::CheckedCastBranchInst: case SILInstructionKind::CheckedCastValueBranchInst: diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index d0147d0ee54e6..097a29ad66bc4 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -1852,6 +1852,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, qualifier); break; } + case SILInstructionKind::AssignByDelegateInst: + llvm_unreachable("not supported"); case SILInstructionKind::BindMemoryInst: { assert(RecordKind == SIL_ONE_TYPE_VALUES && "Layout should be OneTypeValues."); diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 4a596519037ed..125b5f0711c2a 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -1705,6 +1705,8 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { addValueRef(operand)); break; } + case SILInstructionKind::AssignByDelegateInst: + llvm_unreachable("not supported"); case SILInstructionKind::BindMemoryInst: { auto *BI = cast(&SI); SILValue baseOperand = BI->getBase(); diff --git a/test/SILOptimizer/di-property-delegates.swift b/test/SILOptimizer/di-property-delegates.swift new file mode 100644 index 0000000000000..4325f35f4c3c0 --- /dev/null +++ b/test/SILOptimizer/di-property-delegates.swift @@ -0,0 +1,404 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -o %t/a.out +// RUN: %target-run %t/a.out | %FileCheck %s + +// REQUIRES: executable_test + +@propertyDelegate +struct Wrapper { + var value: T { + didSet { + print(" .. set \(value)") + } + } + + init(initialValue: T) { + print(" .. init \(initialValue)") + self.value = initialValue + } +} + +@propertyDelegate +final class ClassWrapper { + var value: T { + didSet { + print(" .. set \(value)") + } + } + + init(initialValue: T) { + print(" .. init \(initialValue)") + self.value = initialValue + } + + deinit { + print(" .. deinit \(value)") + } +} + +protocol IntInitializable { + init(_: Int) +} + +final class Payload : CustomStringConvertible, IntInitializable { + let payload: Int + + init(_ p: Int) { + self.payload = p + print(" + payload alloc \(payload)") + } + + deinit { + print(" - payload free \(payload)") + } + + var description: String { + return "value = \(payload)" + } +} + +struct IntStruct { + var wrapped: Int by Wrapper + + init() { + wrapped = 42 + wrapped = 27 + } + + init(conditional b: Bool) { + if b { + self.$wrapped = Wrapper(initialValue: 32) + } else { + wrapped = 42 + } + } + + init(dynamic b: Bool) { + if b { + wrapped = 42 + } + wrapped = 27 + } +} + +final class IntClass { + var wrapped: Int by Wrapper + + init() { + wrapped = 42 + wrapped = 27 + } + + init(conditional b: Bool) { + if b { + self.$wrapped = Wrapper(initialValue: 32) + } else { + wrapped = 42 + } + } + + init(dynamic b: Bool) { + if b { + wrapped = 42 + } + wrapped = 27 + } +} + +struct RefStruct { + var wrapped: Payload by Wrapper + + init() { + wrapped = Payload(42) + wrapped = Payload(27) + } + + init(conditional b: Bool) { + if b { + self.$wrapped = Wrapper(initialValue: Payload(32)) + } else { + wrapped = Payload(42) + } + } + + init(dynamic b: Bool) { + if b { + wrapped = Payload(42) + } + wrapped = Payload(27) + } +} + +final class GenericClass { + var wrapped: T by Wrapper + + init() { + wrapped = T(42) + wrapped = T(27) + } + + init(conditional b: Bool) { + if b { + self.$wrapped = Wrapper(initialValue: T(32)) + } else { + wrapped = T(42) + } + } + + init(dynamic b: Bool) { + if b { + wrapped = T(42) + } + wrapped = T(27) + } +} + +/* + +This does not work yet. + +struct IntStructWithClassWrapper { + var wrapped: Int by ClassWrapper + + init() { + wrapped = 42 + wrapped = 27 + } + + init(conditional b: Bool) { + if b { + self.$wrapped = ClassWrapper(initialValue: 32) + } else { + wrapped = 42 + } + } + + init(dynamic b: Bool) { + if b { + wrapped = 42 + } + wrapped = 27 + } +} +*/ + +func testIntStruct() { + // CHECK: ## IntStruct + print("\n## IntStruct") + + // CHECK-NEXT: .. init 42 + // CHECK-NEXT: .. set 27 + let t1 = IntStruct() + // CHECK-NEXT: 27 + print(t1.wrapped) + + // CHECK-NEXT: .. init 42 + let t2 = IntStruct(conditional: false) + // CHECK-NEXT: 42 + print(t2.wrapped) + + // CHECK-NEXT: .. init 32 + let t3 = IntStruct(conditional: true) + // CHECK-NEXT: 32 + print(t3.wrapped) + + // CHECK-NEXT: .. init 27 + let t4 = IntStruct(dynamic: false) + // CHECK-NEXT: 27 + print(t4.wrapped) + + // CHECK-NEXT: .. init 42 + // CHECK-NEXT: .. init 27 + let t5 = IntStruct(dynamic: true) + // CHECK-NEXT: 27 + print(t5.wrapped) +} + +func testIntClass() { + // CHECK: ## IntClass + print("\n## IntClass") + + // CHECK-NEXT: .. init 42 + // CHECK-NEXT: .. set 27 + let t1 = IntClass() + // CHECK-NEXT: 27 + print(t1.wrapped) + + // CHECK-NEXT: .. init 42 + let t2 = IntClass(conditional: false) + // CHECK-NEXT: 42 + print(t2.wrapped) + + // CHECK-NEXT: .. init 32 + let t3 = IntClass(conditional: true) + // CHECK-NEXT: 32 + print(t3.wrapped) + + // CHECK-NEXT: .. init 27 + let t4 = IntClass(dynamic: false) + // CHECK-NEXT: 27 + print(t4.wrapped) + + // CHECK-NEXT: .. init 42 + // CHECK-NEXT: .. init 27 + let t5 = IntClass(dynamic: true) + // CHECK-NEXT: 27 + print(t5.wrapped) +} + +func testRefStruct() { + // CHECK: ## RefStruct + print("\n## RefStruct") + + if true { + // CHECK-NEXT: + payload alloc 42 + // CHECK-NEXT: .. init value = 42 + // CHECK-NEXT: + payload alloc 27 + // CHECK-NEXT: .. set value = 27 + // CHECK-NEXT: - payload free 42 + let t1 = RefStruct() + // CHECK-NEXT: value = 27 + print(t1.wrapped) + // CHECK-NEXT: - payload free 27 + } + if true { + // CHECK-NEXT: + payload alloc 42 + // CHECK-NEXT: .. init value = 42 + let t2 = RefStruct(conditional: false) + // CHECK-NEXT: value = 42 + print(t2.wrapped) + // CHECK-NEXT: - payload free 42 + } + if true { + // CHECK-NEXT: + payload alloc 32 + // CHECK-NEXT: .. init value = 32 + let t3 = RefStruct(conditional: true) + // CHECK-NEXT: value = 32 + print(t3.wrapped) + // CHECK-NEXT: - payload free 32 + } + if true { + // CHECK-NEXT: + payload alloc 27 + // CHECK-NEXT: .. init value = 27 + let t4 = RefStruct(dynamic: false) + // CHECK-NEXT: value = 27 + print(t4.wrapped) + // CHECK-NEXT: - payload free 27 + } + if true { + // CHECK-NEXT: + payload alloc 42 + // CHECK-NEXT: .. init value = 42 + // CHECK-NEXT: + payload alloc 27 + // CHECK-NEXT: - payload free 42 + // CHECK-NEXT: .. init value = 27 + let t5 = RefStruct(dynamic: true) + // CHECK-NEXT: value = 27 + print(t5.wrapped) + // CHECK-NEXT: - payload free 27 + } +} + +func testGenericClass() { + // CHECK: ## GenericClass + print("\n## GenericClass") + + if true { + // CHECK-NEXT: + payload alloc 42 + // CHECK-NEXT: .. init value = 42 + // CHECK-NEXT: + payload alloc 27 + // CHECK-NEXT: .. set value = 27 + // CHECK-NEXT: - payload free 42 + let t1 = GenericClass() + // CHECK-NEXT: value = 27 + print(t1.wrapped) + // CHECK-NEXT: - payload free 27 + } + if true { + // CHECK-NEXT: + payload alloc 42 + // CHECK-NEXT: .. init value = 42 + let t2 = GenericClass(conditional: false) + // CHECK-NEXT: value = 42 + print(t2.wrapped) + // CHECK-NEXT: - payload free 42 + } + if true { + // CHECK-NEXT: + payload alloc 32 + // CHECK-NEXT: .. init value = 32 + let t3 = GenericClass(conditional: true) + // CHECK-NEXT: value = 32 + print(t3.wrapped) + // CHECK-NEXT: - payload free 32 + } + if true { + // CHECK-NEXT: + payload alloc 27 + // CHECK-NEXT: .. init value = 27 + let t4 = GenericClass(dynamic: false) + // CHECK-NEXT: value = 27 + print(t4.wrapped) + // CHECK-NEXT: - payload free 27 + } + if true { + // CHECK-NEXT: + payload alloc 42 + // CHECK-NEXT: .. init value = 42 + // CHECK-NEXT: + payload alloc 27 + // CHECK-NEXT: - payload free 42 + // CHECK-NEXT: .. init value = 27 + let t5 = GenericClass(dynamic: true) + // CHECK-NEXT: value = 27 + print(t5.wrapped) + // CHECK-NEXT: - payload free 27 + } +} + +/* +func testIntStructWithClassWrapper() { + // CHECK-DISABLED: ## IntStructWithClassWrapper + print("\n## IntStructWithClassWrapper") + + if true { + // CHECK-NEXT-DISABLED: .. init 42 + // CHECK-NEXT-DISABLED: .. set 27 + let t1 = IntStructWithClassWrapper() + // CHECK-NEXT-DISABLED: 27 + print(t1.wrapped) + // CHECK-NEXT-DISABLED: .. deinit 27 + } + if true { + // CHECK-NEXT-DISABLED: .. init 42 + let t2 = IntStructWithClassWrapper(conditional: false) + // CHECK-NEXT-DISABLED: 42 + print(t2.wrapped) + // CHECK-NEXT-DISABLED: .. deinit 42 + } + if true { + // CHECK-NEXT-DISABLED: .. init 32 + let t3 = IntStructWithClassWrapper(conditional: true) + // CHECK-NEXT-DISABLED: 32 + print(t3.wrapped) + // CHECK-NEXT-DISABLED: .. deinit 32 + } + if true { + // CHECK-NEXT-DISABLED: .. init 27 + let t4 = IntStructWithClassWrapper(dynamic: false) + // CHECK-NEXT-DISABLED: 27 + print(t4.wrapped) + // CHECK-NEXT-DISABLED: .. deinit 27 + } + if true { + // CHECK-NEXT-DISABLED: .. init 42 + // CHECK-NEXT-DISABLED: .. deinit 42 + // CHECK-NEXT-DISABLED: .. init 27 + let t5 = IntStructWithClassWrapper(dynamic: true) + // CHECK-NEXT-DISABLED: 27 + print(t5.wrapped) + // CHECK-NEXT-DISABLED: .. deinit 27 + } +} +*/ + +testIntStruct() +testIntClass() +testRefStruct() +testGenericClass() +//testIntStructWithClassWrapper() +