diff --git a/include/swift/Basic/Generators.h b/include/swift/Basic/Generators.h index df6051162b926..748711b0eec7f 100644 --- a/include/swift/Basic/Generators.h +++ b/include/swift/Basic/Generators.h @@ -31,6 +31,10 @@ // concept SimpleGenerator : Generator { // type reference; // +// // Get the current value. +// reference get(); +// +// // Get the current value and then advance the generator. // reference claimNext(); // } // @@ -103,7 +107,7 @@ class ArrayRefGenerator { } /// Return the current element of the array. - reference getCurrent() const { + reference get() const { assert(!isFinished()); return values.front(); } @@ -111,7 +115,7 @@ class ArrayRefGenerator { /// Claim the current element of the array and advance past it. reference claimNext() { assert(!isFinished()); - reference result = getCurrent(); + reference result = get(); advance(); return result; } diff --git a/include/swift/SIL/AbstractionPattern.h b/include/swift/SIL/AbstractionPattern.h index e9771df716956..3ece2ef05de9c 100644 --- a/include/swift/SIL/AbstractionPattern.h +++ b/include/swift/SIL/AbstractionPattern.h @@ -36,6 +36,7 @@ namespace clang { namespace swift { namespace Lowering { class FunctionParamGenerator; +class TupleElementGenerator; /// A pattern for the abstraction of a value. /// @@ -783,7 +784,8 @@ class AbstractionPattern { /// Note that, for most purposes, you should lower a field's type against its /// *unsubstituted* interface type. AbstractionPattern - unsafeGetSubstFieldType(ValueDecl *member, CanType origMemberType) const; + unsafeGetSubstFieldType(ValueDecl *member, CanType origMemberType, + SubstitutionMap subMap) const; private: /// Return an abstraction pattern for the curried type of an @@ -1173,6 +1175,10 @@ class AbstractionPattern { return CXXMethod; } + bool isOpaqueTuple() const { + return getKind() == Kind::Tuple; + } + bool isOpaqueFunctionOrOpaqueDerivativeFunction() const { return (getKind() == Kind::OpaqueFunction || getKind() == Kind::OpaqueDerivativeFunction); @@ -1355,20 +1361,8 @@ class AbstractionPattern { /// expand to. /// /// This pattern must be a tuple pattern. - /// - /// Calls handleScalar or handleExpansion as appropriate for each - /// element of the original tuple, in order. void forEachTupleElement(CanTupleType substType, - llvm::function_ref - handleScalar, - llvm::function_ref - handleExpansion) const; + llvm::function_ref fn) const; /// Perform a parallel visitation of the elements of a tuple type, /// expanding the elements of the type. This preserves the structure diff --git a/include/swift/SIL/AbstractionPatternGenerators.h b/include/swift/SIL/AbstractionPatternGenerators.h index b0cbd0624f432..e25636e096d05 100644 --- a/include/swift/SIL/AbstractionPatternGenerators.h +++ b/include/swift/SIL/AbstractionPatternGenerators.h @@ -51,7 +51,7 @@ class FunctionParamGenerator { unsigned substParamIndex = 0; /// The number of subst parameters corresponding to the current - /// subst parameter. + /// orig parameter. unsigned numSubstParamsForOrigParam; /// Whether the orig function type is opaque, i.e. does not permit us to @@ -125,7 +125,7 @@ class FunctionParamGenerator { } /// Return whether the current orig parameter type is a pack expansion. - bool isPackExpansion() const { + bool isOrigPackExpansion() const { assert(!isFinished()); return origParamIsExpansion; } @@ -148,6 +148,129 @@ class FunctionParamGenerator { } }; +/// A generator for traversing the formal elements of a tuple type +/// while properly respecting variadic generics. +class TupleElementGenerator { + // The steady state of the generator. + + /// The abstraction pattern of the entire tuple type. Set once + /// during construction. + AbstractionPattern origTupleType; + + /// The substitute tuple type. Set once during construction. + CanTupleType substTupleType; + + /// The number of orig elements to traverse. Set once during + /// construction. + unsigned numOrigElts; + + /// The index of the current orig element. + /// Incremented during advance(). + unsigned origEltIndex = 0; + + /// The (start) index of the current subst elements. + /// Incremented during advance(). + unsigned substEltIndex = 0; + + /// The number of subst elements corresponding to the current + /// orig element. + unsigned numSubstEltsForOrigElt; + + /// Whether the orig tuple type is opaque, i.e. does not permit us to + /// call getNumTupleElements() and similar accessors. Set once during + /// construction. + bool origTupleTypeIsOpaque; + + /// Whether the current orig element is a pack expansion. + bool origEltIsExpansion; + + /// The abstraction pattern of the current orig element. + /// If it is a pack expansion, this is the expansion type, not the + /// pattern type. + AbstractionPattern origEltType = AbstractionPattern::getInvalid(); + + /// Load the informaton for the current orig element into the + /// fields above for it. + void loadElement() { + origEltType = origTupleType.getTupleElementType(origEltIndex); + origEltIsExpansion = origEltType.isPackExpansion(); + numSubstEltsForOrigElt = + (origEltIsExpansion + ? origEltType.getNumPackExpandedComponents() + : 1); + } + +public: + TupleElementGenerator(AbstractionPattern origTupleType, + CanTupleType substTupleType); + + /// Is the traversal finished? If so, none of the getters below + /// are allowed to be called. + bool isFinished() const { + return origEltIndex == numOrigElts; + } + + /// Advance to the next orig element. + void advance() { + assert(!isFinished()); + origEltIndex++; + substEltIndex += numSubstEltsForOrigElt; + if (!isFinished()) loadElement(); + } + + /// Return the index of the current orig element. + unsigned getOrigIndex() const { + assert(!isFinished()); + return origEltIndex; + } + + /// Return the index of the (first) subst element corresponding + /// to the current orig element. + unsigned getSubstIndex() const { + assert(!isFinished()); + return origEltIndex; + } + + /// Return a tuple element for the current orig element. + TupleTypeElt getOrigElement() const { + assert(!isFinished()); + return (origTupleTypeIsOpaque + ? substTupleType->getElement(substEltIndex) + : cast(origTupleType.getType()) + ->getElement(origEltIndex)); + } + + /// Return the type of the current orig element. + const AbstractionPattern &getOrigType() const { + assert(!isFinished()); + return origEltType; + } + + /// Return whether the current orig element type is a pack expansion. + bool isOrigPackExpansion() const { + assert(!isFinished()); + return origEltIsExpansion; + } + + /// Return the substituted elements corresponding to the current + /// orig element type. If the current orig element is not a + /// pack expansion, this will have exactly one element. + CanTupleEltTypeArrayRef getSubstTypes() const { + assert(!isFinished()); + return substTupleType.getElementTypes().slice(substEltIndex, + numSubstEltsForOrigElt); + } + + /// Call this to finalize the traversal and assert that it was done + /// properly. + void finish() { + assert(isFinished() && "didn't finish the traversal"); + assert(substEltIndex == substTupleType->getNumElements() && + "didn't exhaust subst elements; possible missing subs on " + "orig tuple type"); + } +}; + } // end namespace Lowering } // end namespace swift diff --git a/lib/SIL/IR/AbstractionPattern.cpp b/lib/SIL/IR/AbstractionPattern.cpp index 5ce151a7cf576..6f8e375959366 100644 --- a/lib/SIL/IR/AbstractionPattern.cpp +++ b/lib/SIL/IR/AbstractionPattern.cpp @@ -470,35 +470,25 @@ bool AbstractionPattern::doesTupleContainPackExpansionType() const { } void AbstractionPattern::forEachTupleElement(CanTupleType substType, - llvm::function_ref - handleScalar, - llvm::function_ref - handleExpansion) const { - assert(isTuple() && "can only call on a tuple expansion"); - assert(matchesTuple(substType)); - - size_t substEltIndex = 0; - auto substEltTypes = substType.getElementTypes(); - for (size_t origEltIndex : range(getNumTupleElements())) { - auto origEltType = getTupleElementType(origEltIndex); - if (!origEltType.isPackExpansion()) { - handleScalar(origEltIndex, substEltIndex, - origEltType, substEltTypes[substEltIndex]); - substEltIndex++; - } else { - auto numComponents = origEltType.getNumPackExpandedComponents(); - handleExpansion(origEltIndex, substEltIndex, origEltType, - substEltTypes.slice(substEltIndex, numComponents)); - substEltIndex += numComponents; - } + llvm::function_ref handleElement) const { + TupleElementGenerator elt(*this, substType); + for (; !elt.isFinished(); elt.advance()) { + handleElement(elt); } - assert(substEltIndex == substEltTypes.size()); + elt.finish(); +} + +TupleElementGenerator::TupleElementGenerator( + AbstractionPattern origTupleType, + CanTupleType substTupleType) + : origTupleType(origTupleType), substTupleType(substTupleType) { + assert(origTupleType.isTuple()); + assert(origTupleType.matchesTuple(substTupleType)); + + origTupleTypeIsOpaque = origTupleType.isOpaqueTuple(); + numOrigElts = origTupleType.getNumTupleElements(); + + if (!isFinished()) loadElement(); } void AbstractionPattern::forEachExpandedTupleElement(CanTupleType substType, @@ -1376,6 +1366,26 @@ void AbstractionPattern::dump() const { llvm::errs() << "\n"; } +static void printGenerics(raw_ostream &out, const AbstractionPattern &pattern) { + if (auto sig = pattern.getGenericSignature()) { + sig->print(out); + } + // It'd be really nice if we could get these interleaved with the types. + if (auto subs = pattern.getGenericSubstitutions()) { + out << "@<"; + bool first = false; + for (auto sub : subs.getReplacementTypes()) { + if (!first) { + out << ","; + } else { + first = true; + } + out << sub; + } + out << ">"; + } +} + void AbstractionPattern::print(raw_ostream &out) const { switch (getKind()) { case Kind::Invalid: @@ -1396,9 +1406,7 @@ void AbstractionPattern::print(raw_ostream &out) const { ? "AP::Type" : getKind() == Kind::Discard ? "AP::Discard" : "<>"); - if (auto sig = getGenericSignature()) { - sig->print(out); - } + printGenerics(out, *this); out << '('; getType().dump(out); out << ')'; @@ -1425,9 +1433,7 @@ void AbstractionPattern::print(raw_ostream &out) const { getKind() == Kind::ObjCCompletionHandlerArgumentsType ? "AP::ObjCCompletionHandlerArgumentsType(" : "AP::CFunctionAsMethodType("); - if (auto sig = getGenericSignature()) { - sig->print(out); - } + printGenerics(out, *this); getType().dump(out); out << ", "; // [TODO: Improve-Clang-type-printing] @@ -1459,9 +1465,7 @@ void AbstractionPattern::print(raw_ostream &out) const { getKind() == Kind::CurriedCXXMethodType ? "AP::CurriedCXXMethodType(" : "AP::PartialCurriedCXXMethodType"); - if (auto sig = getGenericSignature()) { - sig->print(out); - } + printGenerics(out, *this); getType().dump(out); out << ", "; getCXXMethod()->dump(); @@ -1569,13 +1573,14 @@ bool AbstractionPattern::hasSameBasicTypeStructure(CanType l, CanType r) { AbstractionPattern AbstractionPattern::unsafeGetSubstFieldType(ValueDecl *member, - CanType origMemberInterfaceType) + CanType origMemberInterfaceType, + SubstitutionMap subMap) const { assert(origMemberInterfaceType); if (isTypeParameterOrOpaqueArchetype()) { // Fall back to the generic abstraction pattern for the member. auto sig = member->getDeclContext()->getGenericSignatureOfContext(); - return AbstractionPattern(sig.getCanonicalSignature(), + return AbstractionPattern(subMap, sig.getCanonicalSignature(), origMemberInterfaceType); } @@ -1612,7 +1617,9 @@ const { member, origMemberInterfaceType) ->getReducedType(getGenericSignature()); - return AbstractionPattern(getGenericSignature(), memberTy); + return AbstractionPattern(getGenericSubstitutions(), + getGenericSignature(), + memberTy); } llvm_unreachable("invalid abstraction pattern kind"); } @@ -2179,28 +2186,19 @@ class SubstFunctionTypePatternVisitor CanType visitTupleType(CanTupleType tuple, AbstractionPattern pattern) { assert(pattern.isTuple()); - // It's pretty weird for us to end up in this case with an - // open-coded tuple pattern, but it happens with opaque derivative - // functions in autodiff. - CanTupleType origTupleTypeForLabels = pattern.getAs(); - if (!origTupleTypeForLabels) origTupleTypeForLabels = tuple; - SmallVector tupleElts; - pattern.forEachTupleElement(tuple, - [&](unsigned origEltIndex, unsigned substEltIndex, - AbstractionPattern origEltType, CanType substEltType) { - auto eltTy = visit(substEltType, origEltType); - auto &origElt = origTupleTypeForLabels->getElement(origEltIndex); - tupleElts.push_back(origElt.getWithType(eltTy)); - }, [&](unsigned origEltIndex, unsigned substEltIndex, - AbstractionPattern origExpansionType, - CanTupleEltTypeArrayRef substEltTypes) { - CanType candidateSubstType; - if (!substEltTypes.empty()) - candidateSubstType = substEltTypes[0]; - auto eltTy = handlePackExpansion(origExpansionType, candidateSubstType); - auto &origElt = origTupleTypeForLabels->getElement(origEltIndex); - tupleElts.push_back(origElt.getWithType(eltTy)); + pattern.forEachTupleElement(tuple, [&](TupleElementGenerator &elt) { + auto substEltTypes = elt.getSubstTypes(); + CanType eltTy; + if (!elt.isOrigPackExpansion()) { + eltTy = visit(substEltTypes[0], elt.getOrigType()); + } else { + CanType candidateSubstType; + if (!substEltTypes.empty()) + candidateSubstType = substEltTypes[0]; + eltTy = handlePackExpansion(elt.getOrigType(), candidateSubstType); + } + tupleElts.push_back(elt.getOrigElement().getWithType(eltTy)); }); return CanType(TupleType::get(tupleElts, TC.Context)); @@ -2219,7 +2217,7 @@ class SubstFunctionTypePatternVisitor pattern.forEachFunctionParam(func.getParams(), /*ignore self*/ false, [&](FunctionParamGenerator ¶m) { - if (!param.isPackExpansion()) { + if (!param.isOrigPackExpansion()) { auto newParamTy = visit(param.getSubstParams()[0].getParameterType(), param.getOrigType()); addParam(param.getOrigFlags(), newParamTy); diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 4ed98fb82dcf6..749b4cff0ed09 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -1274,21 +1274,21 @@ class DestructureResults { if (origType.isTuple()) { auto substTupleType = cast(substType); origType.forEachTupleElement(substTupleType, - [&](unsigned origEltIndex, unsigned substEltIndex, - AbstractionPattern origEltType, CanType substEltType) { + [&](TupleElementGenerator &elt) { // If the original element type is not a pack expansion, just // pull off the next substituted element type. - destructure(origEltType, substEltType); + if (!elt.isOrigPackExpansion()) { + destructure(elt.getOrigType(), elt.getSubstTypes()[0]); + return; + } - }, [&](unsigned origEltIndex, unsigned substEltIndex, - AbstractionPattern origExpansionType, - CanTupleEltTypeArrayRef substEltTypes) { // If the original element type is a pack expansion, build a // lowered pack type for the substituted components it expands to. + auto origExpansionType = elt.getOrigType(); bool indirect = origExpansionType.arePackElementsPassedIndirectly(TC); SmallVector packElts; - for (auto substEltType : substEltTypes) { + for (auto substEltType : elt.getSubstTypes()) { auto origComponentType = origExpansionType.getPackExpansionComponentType(substEltType); CanType loweredEltTy = @@ -1576,7 +1576,7 @@ class DestructureInputs { // If the parameter is not a pack expansion, just pull off the // next parameter and destructure it in parallel with the abstraction // pattern for the type. - if (!param.isPackExpansion()) { + if (!param.isOrigPackExpansion()) { visit(param.getOrigType(), param.getSubstParams()[0], /*forSelf*/false); return; @@ -1690,16 +1690,17 @@ class DestructureInputs { assert(ownership != ValueOwnership::InOut); assert(origType.isTuple()); - origType.forEachTupleElement(substType, - [&](unsigned origEltIndex, unsigned substEltIndex, - AbstractionPattern origEltType, CanType substEltType) { - visit(ownership, forSelf, origEltType, substEltType, - isNonDifferentiable); - }, [&](unsigned origEltIndex, unsigned substEltIndex, - AbstractionPattern origExpansionType, - CanTupleEltTypeArrayRef substEltTypes) { + origType.forEachTupleElement(substType, [&](TupleElementGenerator &elt) { + if (!elt.isOrigPackExpansion()) { + visit(ownership, forSelf, elt.getOrigType(), elt.getSubstTypes()[0], + isNonDifferentiable); + return; + } + + auto origExpansionType = elt.getOrigType(); + SmallVector packElts; - for (auto substEltType : substEltTypes) { + for (auto substEltType : elt.getSubstTypes()) { auto origComponentType = origExpansionType.getPackExpansionComponentType(substEltType); auto loweredEltTy = @@ -4748,7 +4749,17 @@ class SILTypeSubstituter : return origType; } - AbstractionPattern abstraction(Sig, origType); + // We've looked through all the top-level structure in the orig + // type that's affected by type lowering. If substitution has + // given us a type with top-level structure that's affected by + // type lowering, it must be because the orig type was a type + // variable of some sort, and we should lower using an opaque + // abstraction pattern. If substitution hasn't given us such a + // type, it doesn't matter what abstraction pattern we use, + // lowering will just come back with substType. So we can just + // use an opaque abstraction pattern here and not put any effort + // into computing a more "honest" abstraction pattern. + AbstractionPattern abstraction = AbstractionPattern::getOpaque(); return TC.getLoweredRValueType(typeExpansionContext, abstraction, substType); } diff --git a/lib/SIL/IR/SILType.cpp b/lib/SIL/IR/SILType.cpp index e4ee158320678..a0cbcdb204453 100644 --- a/lib/SIL/IR/SILType.cpp +++ b/lib/SIL/IR/SILType.cpp @@ -301,9 +301,30 @@ bool SILType::canRefCast(SILType operTy, SILType resultTy, SILModule &M) { && toTy.isHeapObjectReferenceType(); } +static bool needsFieldSubstitutions(const AbstractionPattern &origType) { + if (origType.isTypeParameter()) return false; + auto type = origType.getType(); + if (!type->hasTypeParameter()) return false; + return type.findIf([](CanType type) { + return isa(type); + }); +} + +static void addFieldSubstitutionsIfNeeded(TypeConverter &TC, SILType ty, + ValueDecl *field, + AbstractionPattern &origType) { + if (needsFieldSubstitutions(origType)) { + auto subMap = ty.getASTType()->getContextSubstitutionMap( + &TC.M, field->getDeclContext()); + origType = origType.withSubstitutions(subMap); + } +} + SILType SILType::getFieldType(VarDecl *field, TypeConverter &TC, TypeExpansionContext context) const { AbstractionPattern origFieldTy = TC.getAbstractionPattern(field); + addFieldSubstitutionsIfNeeded(TC, *this, field, origFieldTy); + CanType substFieldTy; if (field->hasClangNode()) { substFieldTy = origFieldTy.getType(); @@ -372,6 +393,9 @@ SILType SILType::getEnumElementType(EnumElementDecl *elt, TypeConverter &TC, getCategory()); } + auto origEltType = TC.getAbstractionPattern(elt); + addFieldSubstitutionsIfNeeded(TC, *this, elt, origEltType); + auto substEltTy = getASTType()->getTypeOfMember( &TC.M, elt, elt->getArgumentInterfaceType()); auto loweredTy = TC.getLoweredRValueType( diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index fe71adc24b2d0..26bc61f04e11b 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -749,11 +749,12 @@ namespace { RetTy visitTupleType(CanTupleType type, AbstractionPattern origType, IsTypeExpansionSensitive_t isSensitive) { RecursiveProperties props; - for (unsigned i = 0, e = type->getNumElements(); i < e; ++i) { - props.addSubobject(classifyType(origType.getTupleElementType(i), - type.getElementType(i), - TC, Expansion)); - } + origType.forEachExpandedTupleElement(type, + [&](AbstractionPattern origEltType, CanType substEltType, + const TupleTypeElt &elt) { + props.addSubobject( + classifyType(origEltType, substEltType, TC, Expansion)); + }); props = mergeIsTypeExpansionSensitive(isSensitive, props); return asImpl().handleAggregateByProperties(type, props); } @@ -2250,12 +2251,12 @@ namespace { AbstractionPattern origType, IsTypeExpansionSensitive_t isSensitive) { RecursiveProperties properties; - for (unsigned i = 0, e = tupleType->getNumElements(); i < e; ++i) { - auto eltType = tupleType.getElementType(i); - auto origEltType = origType.getTupleElementType(i); - auto &lowering = TC.getTypeLowering(origEltType, eltType, Expansion); - properties.addSubobject(lowering.getRecursiveProperties()); - } + origType.forEachExpandedTupleElement(tupleType, + [&](AbstractionPattern origEltType, CanType substEltType, + const TupleTypeElt &elt) { + properties.addSubobject( + classifyType(origEltType, substEltType, TC, Expansion)); + }); properties = mergeIsTypeExpansionSensitive(isSensitive, properties); return handleAggregateByProperties(tupleType, @@ -2343,7 +2344,8 @@ namespace { auto sig = field->getDeclContext()->getGenericSignatureOfContext(); auto interfaceTy = field->getInterfaceType()->getReducedType(sig); auto origFieldType = origType.unsafeGetSubstFieldType(field, - interfaceTy); + interfaceTy, + subMap); properties.addSubobject(classifyType(origFieldType, substFieldType, TC, Expansion)); @@ -2422,7 +2424,8 @@ namespace { auto origEltType = origType.unsafeGetSubstFieldType(elt, elt->getArgumentInterfaceType() - ->getReducedType(D->getGenericSignature())); + ->getReducedType(D->getGenericSignature()), + subMap); properties.addSubobject(classifyType(origEltType, substEltType, TC, Expansion)); properties = @@ -2765,7 +2768,8 @@ bool TypeConverter::visitAggregateLeaves( auto interfaceTy = structField->getInterfaceType()->getReducedType(sig); auto origFieldType = - origTy.unsafeGetSubstFieldType(structField, interfaceTy); + origTy.unsafeGetSubstFieldType(structField, interfaceTy, + subMap); insertIntoWorklist(substFieldTy, origFieldType, structField, llvm::None); } @@ -2782,7 +2786,7 @@ bool TypeConverter::visitAggregateLeaves( ->getCanonicalType(); auto origElementTy = origTy.unsafeGetSubstFieldType( element, element->getArgumentInterfaceType()->getReducedType( - decl->getGenericSignature())); + decl->getGenericSignature()), subMap); insertIntoWorklist(substElementType, origElementTy, element, llvm::None); diff --git a/lib/SILGen/FunctionInputGenerator.h b/lib/SILGen/FunctionInputGenerator.h index 4131748443427..1af627be8d21c 100644 --- a/lib/SILGen/FunctionInputGenerator.h +++ b/lib/SILGen/FunctionInputGenerator.h @@ -89,7 +89,7 @@ class FunctionInputGenerator { /// Ready the current orig parameter. void readyOrigParameter() { substParamIndex = 0; - if (origParam.isPackExpansion()) { + if (origParam.isOrigPackExpansion()) { // The pack value exists in the lowered parameters and must be // claimed whether it contains formal parameters or not. packValue = inputs.claimNext(); @@ -125,7 +125,7 @@ class FunctionInputGenerator { } bool isOrigPackExpansion() const { - return origParam.isPackExpansion(); + return origParam.isOrigPackExpansion(); } AbstractionPattern getOrigType() const { diff --git a/lib/SILGen/ResultPlan.cpp b/lib/SILGen/ResultPlan.cpp index 5f0e10b2ea774..99875cfc01f7c 100644 --- a/lib/SILGen/ResultPlan.cpp +++ b/lib/SILGen/ResultPlan.cpp @@ -18,6 +18,7 @@ #include "RValue.h" #include "SILGenFunction.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/SIL/AbstractionPatternGenerators.h" using namespace swift; using namespace Lowering; @@ -617,19 +618,20 @@ class TupleInitializationResultPlan final : public ResultPlan { eltPlans.reserve(origType.getNumTupleElements()); origType.forEachTupleElement(substType, - [&](unsigned origEltIndex, unsigned substEltIndex, - AbstractionPattern origEltType, - CanType substEltType) { - Initialization *eltInit = eltInits[substEltIndex].get(); - eltPlans.push_back(builder.build(eltInit, origEltType, substEltType)); - }, - [&](unsigned origEltIndex, unsigned substEltIndex, - AbstractionPattern origExpansionType, - CanTupleEltTypeArrayRef substEltTypes) { - auto componentInits = eltInits.slice(substEltIndex, substEltTypes.size()); - eltPlans.push_back(builder.buildForPackExpansion(componentInits, - origExpansionType, - substEltTypes)); + [&](TupleElementGenerator &elt) { + auto origEltType = elt.getOrigType(); + auto substEltTypes = elt.getSubstTypes(); + if (!elt.isOrigPackExpansion()) { + Initialization *eltInit = eltInits[elt.getSubstIndex()].get(); + eltPlans.push_back(builder.build(eltInit, origEltType, + substEltTypes[0])); + } else { + auto componentInits = + eltInits.slice(elt.getSubstIndex(), substEltTypes.size()); + eltPlans.push_back(builder.buildForPackExpansion(componentInits, + origEltType, + substEltTypes)); + } }); } diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 78eda39d4ce3d..cff8bf88d5e04 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -42,6 +42,7 @@ #include "swift/Basic/SourceManager.h" #include "swift/Basic/Unicode.h" #include "swift/SIL/PrettyStackTrace.h" +#include "swift/SIL/AbstractionPatternGenerators.h" #include "swift/SIL/SILArgument.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" @@ -2203,19 +2204,16 @@ static unsigned getFlattenedValueCount(AbstractionPattern origType, // Otherwise, add up the elements. unsigned count = 0; - origType.forEachTupleElement(substTuple, - [&](unsigned origEltIndex, - unsigned substEltIndex, - AbstractionPattern origEltType, - CanType substEltType) { - // Recursively expand scalar components. - count += getFlattenedValueCount(origEltType, substEltType); - }, [&](unsigned origEltIndex, - unsigned substEltIndex, - AbstractionPattern origExpansionType, - CanTupleEltTypeArrayRef substEltTypes) { + origType.forEachTupleElement(substTuple, [&](TupleElementGenerator &elt) { // Expansion components turn into a single parameter. - count++; + if (elt.isOrigPackExpansion()) { + count++; + + // Recursively expand scalar components. + } else { + count += getFlattenedValueCount(elt.getOrigType(), + elt.getSubstTypes()[0]); + } }); return count; } @@ -3389,10 +3387,23 @@ class ArgEmitter { // If the source expression is a tuple literal, we can break it // up directly. if (auto tuple = dyn_cast(e)) { - for (auto i : indices(tuple->getElements())) { - emit(tuple->getElement(i), - origParamType.getTupleElementType(i)); - } + auto substTupleType = + cast(e->getType()->getCanonicalType()); + origParamType.forEachTupleElement(substTupleType, + [&](TupleElementGenerator &elt) { + if (!elt.isOrigPackExpansion()) { + emit(tuple->getElement(elt.getSubstIndex()), elt.getOrigType()); + return; + } + + auto substEltTypes = elt.getSubstTypes(); + SmallVector eltArgs; + eltArgs.reserve(substEltTypes.size()); + for (auto i : range(elt.getSubstIndex(), substEltTypes.size())) { + eltArgs.emplace_back(tuple->getElement(i)); + } + emitPackArg(eltArgs, elt.getOrigType()); + }); return; } diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 2c7a81ea65dea..aa3210cdc4f6c 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -25,6 +25,7 @@ #include "swift/AST/ParameterList.h" #include "swift/AST/PropertyWrappers.h" #include "swift/Basic/Defer.h" +#include "swift/Basic/Generators.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILUndef.h" @@ -33,6 +34,61 @@ using namespace swift; using namespace Lowering; +namespace { + +class LoweredParamsInContextGenerator { + SILGenFunction &SGF; + ArrayRefGenerator> loweredParams; + +public: + LoweredParamsInContextGenerator(SILGenFunction &SGF) + : SGF(SGF), + loweredParams(SGF.F.getLoweredFunctionType()->getParameters()) { + } + + using reference = SILType; + + /// Get the original (unsubstituted into context) lowered parameter + /// type information. + SILParameterInfo getOrigInfo() const { + return loweredParams.get(); + } + + SILType get() const { + return SGF.getSILTypeInContext(loweredParams.get(), + SGF.F.getLoweredFunctionType()); + } + + SILType claimNext() { + auto param = get(); + advance(); + return param; + } + + bool isFinished() const { + return loweredParams.isFinished(); + } + + void advance() { + loweredParams.advance(); + } + + void finish() { + loweredParams.finish(); + } +}; + +} // end anonymous namespace + +static ManagedValue emitManagedParameter(SILGenFunction &SGF, + SILValue value, bool isOwned) { + if (isOwned) { + return SGF.emitManagedRValueWithCleanup(value); + } else { + return ManagedValue::forUnmanaged(value); + } +} + static SILValue emitConstructorMetatypeArg(SILGenFunction &SGF, ValueDecl *ctor) { // In addition to the declared arguments, the constructor implicitly takes @@ -63,14 +119,65 @@ static SILValue emitConstructorMetatypeArg(SILGenFunction &SGF, static RValue emitImplicitValueConstructorArg(SILGenFunction &SGF, SILLocation loc, CanType interfaceType, - DeclContext *DC) { + DeclContext *DC, + LoweredParamsInContextGenerator &loweredParamTypes, + Initialization *argInit = nullptr) { auto type = DC->mapTypeIntoContext(interfaceType)->getCanonicalType(); // Restructure tuple arguments. - if (auto tupleTy = dyn_cast(interfaceType)) { + if (auto tupleIfaceTy = dyn_cast(interfaceType)) { + // If we don't have a context to emit into, but we have a tuple + // that contains pack expansions, create a temporary. + TemporaryInitializationPtr tempInit; + if (!argInit && tupleIfaceTy.containsPackExpansionType()) { + tempInit = SGF.emitTemporary(loc, SGF.getTypeLowering(type)); + argInit = tempInit.get(); + } + + // Split the initialization into element initializations if we have + // one. We should never have to deal with an initialization that + // can't be split here. + assert(!argInit || argInit->canSplitIntoTupleElements()); + SmallVector initsBuf; + MutableArrayRef eltInits; + if (argInit) { + eltInits = argInit->splitIntoTupleElements(SGF, loc, type, initsBuf); + assert(eltInits.size() == tupleIfaceTy->getNumElements()); + } + RValue tuple(type); - for (auto fieldType : tupleTy.getElementTypes()) - tuple.addElement(emitImplicitValueConstructorArg(SGF, loc, fieldType, DC)); + + for (auto eltIndex : range(tupleIfaceTy->getNumElements())) { + auto eltIfaceType = tupleIfaceTy.getElementType(eltIndex); + auto eltInit = (argInit ? eltInits[eltIndex].get() : nullptr); + RValue element = emitImplicitValueConstructorArg(SGF, loc, eltIfaceType, + DC, loweredParamTypes, + eltInit); + if (argInit) { + assert(element.isInContext()); + } else { + tuple.addElement(std::move(element)); + } + } + + // If we created a temporary initializer above, finish it and claim + // the managed buffer. + if (tempInit) { + tempInit->finishInitialization(SGF); + + auto tupleValue = tempInit->getManagedAddress(); + if (tupleValue.getType().isLoadable(SGF.F)) { + tupleValue = SGF.B.createLoadTake(loc, tupleValue); + } + + return RValue(SGF, loc, type, tupleValue); + + // Otherwise, if we have an emitInto, return forInContext(). + } else if (argInit) { + argInit->finishInitialization(SGF); + return RValue::forInContext(); + } + return tuple; } @@ -83,13 +190,51 @@ static RValue emitImplicitValueConstructorArg(SILGenFunction &SGF, VD->setSpecifier(ParamSpecifier::Default); VD->setInterfaceType(interfaceType); - auto argType = SGF.getLoweredTypeForFunctionArgument(type); + auto origParamInfo = loweredParamTypes.getOrigInfo(); + auto argType = loweredParamTypes.claimNext(); + auto *arg = SGF.F.begin()->createFunctionArgument(argType, VD); - ManagedValue mvArg; - if (arg->getArgumentConvention().isOwnedConvention()) { - mvArg = SGF.emitManagedRValueWithCleanup(arg); - } else { - mvArg = ManagedValue::forUnmanaged(arg); + bool argIsConsumed = origParamInfo.isConsumed(); + + // If the lowered parameter is a pack expansion, copy/move the pack + // into the initialization, which we assume is there. + if (auto packTy = argType.getAs()) { + assert(isa(interfaceType)); + assert(packTy->getNumElements() == 1); + assert(argInit); + assert(argInit->canPerformPackExpansionInitialization()); + + auto expansionTy = packTy->getSILElementType(0); + auto openedEnvAndEltTy = + SGF.createOpenedElementValueEnvironment(expansionTy); + auto openedEnv = openedEnvAndEltTy.first; + auto eltTy = openedEnvAndEltTy.second; + auto formalPackType = CanPackType::get(SGF.getASTContext(), {type}); + + SGF.emitDynamicPackLoop(loc, formalPackType, /*component*/0, openedEnv, + [&](SILValue indexWithinComponent, + SILValue packExpansionIndex, + SILValue packIndex) { + argInit->performPackExpansionInitialization(SGF, loc, + indexWithinComponent, + [&](Initialization *eltInit) { + auto eltAddr = + SGF.B.createPackElementGet(loc, packIndex, arg, eltTy); + ManagedValue eltMV = emitManagedParameter(SGF, eltAddr, argIsConsumed); + eltInit->copyOrInitValueInto(SGF, loc, eltMV, argIsConsumed); + eltInit->finishInitialization(SGF); + }); + }); + argInit->finishInitialization(SGF); + return RValue::forInContext(); + } + + ManagedValue mvArg = emitManagedParameter(SGF, arg, argIsConsumed); + + if (argInit) { + argInit->copyOrInitValueInto(SGF, loc, mvArg, argIsConsumed); + argInit->finishInitialization(SGF); + return RValue::forInContext(); } // This can happen if the value is resilient in the calling convention @@ -164,15 +309,19 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, AssertingManualScope functionLevelScope(SGF.Cleanups, CleanupLocation(Loc)); + auto loweredFunctionTy = SGF.F.getLoweredFunctionType(); + // FIXME: Handle 'self' along with the other arguments. + assert(loweredFunctionTy->getNumResults() == 1); + auto selfResultInfo = loweredFunctionTy->getResults()[0]; auto *paramList = ctor->getParameters(); auto *selfDecl = ctor->getImplicitSelfDecl(); auto selfIfaceTy = selfDecl->getInterfaceType(); - SILType selfTy = SGF.getLoweredTypeForFunctionArgument(selfDecl->getType()); + SILType selfTy = SGF.getSILTypeInContext(selfResultInfo, loweredFunctionTy); // Emit the indirect return argument, if any. SILValue resultSlot; - if (SILModuleConventions::isReturnedIndirectlyInSIL(selfTy, SGF.SGM.M)) { + if (selfTy.isAddress()) { auto &AC = SGF.getASTContext(); auto VD = new (AC) ParamDecl(SourceLoc(), SourceLoc(), AC.getIdentifier("$return_value"), @@ -181,10 +330,11 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, ctor); VD->setSpecifier(ParamSpecifier::InOut); VD->setInterfaceType(selfIfaceTy); - resultSlot = - SGF.F.begin()->createFunctionArgument(selfTy.getAddressType(), VD); + resultSlot = SGF.F.begin()->createFunctionArgument(selfTy, VD); } + LoweredParamsInContextGenerator loweredParams(SGF); + // Emit the elementwise arguments. SmallVector elements; for (size_t i = 0, size = paramList->size(); i < size; ++i) { @@ -192,10 +342,13 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, elements.push_back( emitImplicitValueConstructorArg( - SGF, Loc, param->getInterfaceType()->getCanonicalType(), ctor)); + SGF, Loc, param->getInterfaceType()->getCanonicalType(), ctor, + loweredParams)); } emitConstructorMetatypeArg(SGF, ctor); + (void) loweredParams.claimNext(); + loweredParams.finish(); auto *decl = selfTy.getStructOrBoundGenericStruct(); assert(decl && "not a struct?!"); @@ -601,16 +754,21 @@ void SILGenFunction::emitEnumConstructor(EnumElementDecl *element) { Scope scope(Cleanups, CleanupLoc); + LoweredParamsInContextGenerator loweredParams(*this); + // Emit the exploded constructor argument. ArgumentSource payload; if (element->hasAssociatedValues()) { auto eltArgTy = element->getArgumentInterfaceType()->getCanonicalType(); - RValue arg = emitImplicitValueConstructorArg(*this, Loc, eltArgTy, element); + RValue arg = emitImplicitValueConstructorArg(*this, Loc, eltArgTy, element, + loweredParams); payload = ArgumentSource(Loc, std::move(arg)); } // Emit the metatype argument. emitConstructorMetatypeArg(*this, element); + (void) loweredParams.claimNext(); + loweredParams.finish(); // If possible, emit the enum directly into the indirect return. SGFContext C = (dest ? SGFContext(dest.get()) : SGFContext()); diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index 84dbfab354ce5..f7f64070c152d 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -1094,7 +1094,7 @@ class TranslateArguments { // output type, it corresponds to N formal parameters in the // substituted output type. translateToPackParam will pull off // N substituted formal parameters from the input type. - if (outputParams.isPackExpansion()) { + if (outputParams.isOrigPackExpansion()) { auto outputPackParam = claimNextOutputType(); auto output = translateToPackParam(inputParams, diff --git a/lib/SILGen/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index 34b3da9674438..e10791acb7598 100644 --- a/lib/SILGen/SILGenProlog.cpp +++ b/lib/SILGen/SILGenProlog.cpp @@ -361,24 +361,24 @@ class EmitBBArguments : public CanTypeVisitor elements; - orig.forEachTupleElement(t, - [&](unsigned origEltIndex, unsigned substEltIndex, - AbstractionPattern origEltType, - CanType substEltType) { - auto elt = visit(substEltType, origEltType, - init ? eltInits[substEltIndex].get() : nullptr); - assert((init != nullptr) == (elt.isInContext())); - if (!elt.isInContext()) - elements.push_back(elt); - - if (elt.hasCleanup()) - canBeGuaranteed = false; - }, [&](unsigned origEltIndex, unsigned substEltIndex, - AbstractionPattern origExpansionType, - CanTupleEltTypeArrayRef substEltTypes) { - assert(init); - expandPack(origExpansionType, substEltTypes, substEltIndex, - eltInits, elements); + orig.forEachTupleElement(t, [&](TupleElementGenerator &elt) { + auto origEltType = elt.getOrigType(); + auto substEltTypes = elt.getSubstTypes(); + if (!elt.isOrigPackExpansion()) { + auto eltValue = + visit(substEltTypes[0], origEltType, + init ? eltInits[elt.getSubstIndex()].get() : nullptr); + assert((init != nullptr) == (eltValue.isInContext())); + if (!eltValue.isInContext()) + elements.push_back(eltValue); + + if (eltValue.hasCleanup()) + canBeGuaranteed = false; + } else { + assert(init); + expandPack(origEltType, substEltTypes, elt.getSubstIndex(), + eltInits, elements); + } }); // If we emitted into a context, we're done. diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index 49d0ecb27bfc3..69c9cc0ad6393 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -24,6 +24,7 @@ #include "swift/AST/DiagnosticsSIL.h" #include "swift/Basic/ProfileCounter.h" #include "swift/SIL/BasicBlockUtils.h" +#include "swift/SIL/AbstractionPatternGenerators.h" #include "swift/SIL/SILArgument.h" #include "llvm/Support/SaveAndRestore.h" @@ -581,31 +582,28 @@ prepareIndirectResultInit(SILGenFunction &SGF, SILLocation loc, tupleInit->SubInitializations.reserve(resultTupleType->getNumElements()); origResultType.forEachTupleElement(resultTupleType, - [&](unsigned origEltIndex, - unsigned substEltIndex, - AbstractionPattern origEltType, - CanType substEltType) { - auto eltInit = prepareIndirectResultInit(SGF, loc, fnTypeForResults, - origEltType, substEltType, - allResults, - directResults, - indirectResultAddrs, cleanups); - tupleInit->SubInitializations.push_back(std::move(eltInit)); - }, - [&](unsigned origEltIndex, - unsigned substEltIndex, - AbstractionPattern origExpansionType, - CanTupleEltTypeArrayRef substEltTypes) { - assert(allResults[0].isPack()); - assert(SGF.silConv.isSILIndirect(allResults[0])); - allResults = allResults.slice(1); - - auto packAddr = indirectResultAddrs[0]; - indirectResultAddrs = indirectResultAddrs.slice(1); - - preparePackResultInit(SGF, loc, origExpansionType, substEltTypes, - packAddr, - cleanups, tupleInit->SubInitializations); + [&](TupleElementGenerator &elt) { + if (!elt.isOrigPackExpansion()) { + auto eltInit = prepareIndirectResultInit(SGF, loc, fnTypeForResults, + elt.getOrigType(), + elt.getSubstTypes()[0], + allResults, + directResults, + indirectResultAddrs, + cleanups); + tupleInit->SubInitializations.push_back(std::move(eltInit)); + } else { + assert(allResults[0].isPack()); + assert(SGF.silConv.isSILIndirect(allResults[0])); + allResults = allResults.slice(1); + + auto packAddr = indirectResultAddrs[0]; + indirectResultAddrs = indirectResultAddrs.slice(1); + + preparePackResultInit(SGF, loc, elt.getOrigType(), elt.getSubstTypes(), + packAddr, + cleanups, tupleInit->SubInitializations); + } }); return InitializationPtr(tupleInit); diff --git a/test/SILGen/variadic-generic-tuples.swift b/test/SILGen/variadic-generic-tuples.swift index a0fe0fad7bb03..b1585c5804b98 100644 --- a/test/SILGen/variadic-generic-tuples.swift +++ b/test/SILGen/variadic-generic-tuples.swift @@ -204,3 +204,82 @@ func projectTupleElements(_ value: repeat Wrapper) { let tuple = (repeat (each value).value) } + +func takesVariadicTuple(tuple: (repeat each T)) {} + +// CHECK-LABEL: sil{{.*}} @$s4main28testConcreteVariadicTupleArg1i1sySi_SStF : +// CHECK: [[PACK:%.*]] = alloc_pack $Pack{Int, String} +// CHECK-NEXT: [[I_COPY:%.*]] = alloc_stack $Int +// CHECK-NEXT: store %0 to [trivial] [[I_COPY]] : $*Int +// CHECK-NEXT: [[I_INDEX:%.*]] = scalar_pack_index 0 of $Pack{Int, String} +// CHECK-NEXT: pack_element_set [[I_COPY]] : $*Int into [[I_INDEX]] of [[PACK]] : +// CHECK-NEXT: [[S_COPY:%.*]] = alloc_stack $String +// CHECK-NEXT: [[T0:%.*]] = copy_value %1 : $String +// CHECK-NEXT: store [[T0]] to [init] [[S_COPY]] : $*String +// CHECK-NEXT: [[S_INDEX:%.*]] = scalar_pack_index 1 of $Pack{Int, String} +// CHECK-NEXT: pack_element_set [[S_COPY]] : $*String into [[S_INDEX]] of [[PACK]] : +// CHECK-NEXT: // function_ref +// CHECK-NEXT: [[FN:%.*]] = function_ref @$s4main18takesVariadicTuple5tupleyxxQp_t_tRvzlF : $@convention(thin) (@pack_guaranteed Pack{repeat each τ_0_0}) -> () +// CHECK-NEXT: apply [[FN]]([[PACK]]) +// CHECK-NEXT: destroy_addr [[S_COPY]] : +// CHECK-NEXT: dealloc_stack [[S_COPY]] : +// CHECK-NEXT: dealloc_stack [[I_COPY]] : +// CHECK-NEXT: dealloc_pack [[PACK]] : +func testConcreteVariadicTupleArg(i: Int, s: String) { + takesVariadicTuple(tuple: (i, s)) +} + +struct TupleHolder { + var content: (repeat each T) + + // Suppress the memberwise initializer + init(values: repeat each T) { + content = (repeat each values) + } +} + +// CHECK-LABEL: sil{{.*}} @$s4main31takesConcreteTupleHolderFactory7factoryyAA0dE0VySi_SSQPGyXE_tF : +// CHECK-SAME: $@convention(thin) (@guaranteed @noescape @callee_guaranteed () -> @owned TupleHolder) -> () +// CHECK: [[T0:%.*]] = copy_value %0 : +// CHECK: [[T1:%.*]] = begin_borrow [[T0]] +// CHECK: [[RESULT:%.*]] = apply [[T1]]() : +// CHECK: destroy_value [[RESULT]] +func takesConcreteTupleHolderFactory(factory: () -> TupleHolder) { + let holder = factory() +} + +struct MemberwiseTupleHolder { + var content: (repeat each T) +} + +// Memberwise initializer. +// TODO: initialize directly into the fields +// CHECK-LABEL: sil{{.*}} @$s4main21MemberwiseTupleHolderV7contentACyxxQp_QPGxxQp_t_tcfC +// CHECK-SAME: $@convention(method) (@pack_owned Pack{repeat each T}, @thin MemberwiseTupleHolder.Type) -> @out MemberwiseTupleHolder { +// CHECK: [[TEMP:%.*]] = alloc_stack $(repeat each T) +// CHECK-NEXT: [[ZERO:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK-NEXT: [[ONE:%.*]] = integer_literal $Builtin.Word, 1 +// CHECK-NEXT: [[LEN:%.*]] = pack_length $Pack{repeat each T} +// CHECK-NEXT: br bb1([[ZERO]] : $Builtin.Word) +// CHECK: bb1([[IDX:%.*]] : $Builtin.Word) +// CHECK-NEXT: [[IDX_EQ_LEN:%.*]] = builtin "cmp_eq_Word"([[IDX]] : $Builtin.Word, [[LEN]] : $Builtin.Word) : $Builtin.Int1 +// CHECK-NEXT: cond_br [[IDX_EQ_LEN]], bb3, bb2 +// CHECK: bb2: +// CHECK-NEXT: [[INDEX:%.*]] = dynamic_pack_index [[IDX]] of $Pack{repeat each T} +// CHECK-NEXT: open_pack_element [[INDEX]] of at , shape $T, uuid [[UUID:".*"]] +// CHECK-NEXT: [[TUPLE_ELT_ADDR:%.*]] = tuple_pack_element_addr [[INDEX]] of [[TEMP]] : $*(repeat each T) as $*@pack_element([[UUID]]) T +// CHECK-NEXT: [[PACK_ELT_ADDR:%.*]] = pack_element_get [[INDEX]] of %1 : $*Pack{repeat each T} as $*@pack_element([[UUID]]) T +// CHECK-NEXT: copy_addr [take] [[PACK_ELT_ADDR]] to [init] [[TUPLE_ELT_ADDR]] +// CHECK-NEXT: [[NEXT_IDX:%.*]] = builtin "add_Word"([[IDX]] : $Builtin.Word, [[ONE]] : $Builtin.Word) : $Builtin.Word +// CHECK-NEXT: br bb1([[NEXT_IDX]] : $Builtin.Word) +// CHECK: bb3: +// CHECK-NEXT: [[CONTENTS_ADDR:%.*]] = struct_element_addr %0 : $*MemberwiseTupleHolder, #MemberwiseTupleHolder.content +// CHECK-NEXT: copy_addr [take] [[TEMP]] to [init] [[CONTENTS_ADDR]] +// CHECK-NEXT: tuple () +// CHECK-NEXT: dealloc_stack [[TEMP]] +// CHECK-NEXT: return + + +func callVariadicMemberwiseInit() -> MemberwiseTupleHolder { + return MemberwiseTupleHolder(content: (0, "hello")) +}