From bbc682822a396f95dda65a4fa4234bd0b1a00b73 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 22 Mar 2023 01:42:58 -0400 Subject: [PATCH 01/11] [NFC] Change the printing of AbstractionPattern to include the sub map --- lib/SIL/IR/AbstractionPattern.cpp | 32 ++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/lib/SIL/IR/AbstractionPattern.cpp b/lib/SIL/IR/AbstractionPattern.cpp index 5ce151a7cf576..92bff9908b4e2 100644 --- a/lib/SIL/IR/AbstractionPattern.cpp +++ b/lib/SIL/IR/AbstractionPattern.cpp @@ -1376,6 +1376,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 +1416,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 +1443,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 +1475,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(); From f99efc2f9432d2eb56b2a3e3979f8aefce7d2673 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 22 Mar 2023 01:45:27 -0400 Subject: [PATCH 02/11] Fix unsafeGetSubstFieldType to propagate a substitution map in whichever case it happens to be in. This is a basic fix so that parallel walks on tuples and function types in the substituted type will work . Separately, though, I do not think the places that use this really need to be passed an orig type; this is used for computing type properties, and I am not aware of any reason we should need an orig type to compute type properties. Additionally, the orig types computed by this function are not really correct because of the substitution being done in some cases, so it'd be very nice to rip this all out. I'm not good to look into that right now, though. --- include/swift/SIL/AbstractionPattern.h | 3 ++- lib/SIL/IR/AbstractionPattern.cpp | 9 ++++++--- lib/SIL/IR/TypeLowering.cpp | 11 +++++++---- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/include/swift/SIL/AbstractionPattern.h b/include/swift/SIL/AbstractionPattern.h index e9771df716956..6f31004f6233e 100644 --- a/include/swift/SIL/AbstractionPattern.h +++ b/include/swift/SIL/AbstractionPattern.h @@ -783,7 +783,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 diff --git a/lib/SIL/IR/AbstractionPattern.cpp b/lib/SIL/IR/AbstractionPattern.cpp index 92bff9908b4e2..1896258930562 100644 --- a/lib/SIL/IR/AbstractionPattern.cpp +++ b/lib/SIL/IR/AbstractionPattern.cpp @@ -1583,13 +1583,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); } @@ -1626,7 +1627,9 @@ const { member, origMemberInterfaceType) ->getReducedType(getGenericSignature()); - return AbstractionPattern(getGenericSignature(), memberTy); + return AbstractionPattern(getGenericSubstitutions(), + getGenericSignature(), + memberTy); } llvm_unreachable("invalid abstraction pattern kind"); } diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index fe71adc24b2d0..dcb0242bc741b 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -2343,7 +2343,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 +2423,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 +2767,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 +2785,7 @@ bool TypeConverter::visitAggregateLeaves( ->getCanonicalType(); auto origElementTy = origTy.unsafeGetSubstFieldType( element, element->getArgumentInterfaceType()->getReducedType( - decl->getGenericSignature())); + decl->getGenericSignature()), subMap); insertIntoWorklist(substElementType, origElementTy, element, llvm::None); From 8ff61d6a54e9ed7905dbba238db58d9ed8fbf540 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 22 Mar 2023 01:53:35 -0400 Subject: [PATCH 03/11] When we are performing SIL substitution, and we reach a type that needs to be lowered, use an opaque abstraction pattern. As I argue in the comment, we know that the orig type is now either an opaque type or a type with high-level structure that is invariant to lowering. Substitution will not change the latter property, and an opaque abstraction pattern is correct for the former. Attempting to create a "truer" abstraction pattern that preserves more structure from the orig type is both pointless and problematic. The substitutions we just did may have replaced pack references with non-pack types if there are active expansions in progress; this cannot be easily explained in terms of substitutions. (In theory, we could pass a more opaque concept of substitutions through AbstractionPattern, which might help with this. That would also make it harder to catch bugs with signature mismatches, though.) --- lib/SIL/IR/SILFunctionType.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 4ed98fb82dcf6..28f21ac4f49da 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -4748,7 +4748,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); } From a0eeabc88656abaad1c5ae559e6155b8f36e4a8f Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 22 Mar 2023 02:04:05 -0400 Subject: [PATCH 04/11] When computing the field type of a SILType, add substitutions to the orig type. --- lib/SIL/IR/SILType.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) 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( From 9fbecde9a0f9b726945414c322fb6a94ad3a9996 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 22 Mar 2023 02:05:21 -0400 Subject: [PATCH 05/11] Do proper parallel walks of orig+subst types when computing type properties for struct and enum types. --- lib/SIL/IR/TypeLowering.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index dcb0242bc741b..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, From d18b914fc3910d44d2ab4d1aeba7f9fc9f8806ca Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 22 Mar 2023 02:06:15 -0400 Subject: [PATCH 06/11] Do a proper orig+subst walk of tuple expression elements in call argument emission. --- lib/SILGen/SILGenApply.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 78eda39d4ce3d..5fc3d2caa5ed5 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -3389,10 +3389,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, + [&](unsigned origEltIndex, unsigned substEltIndex, + AbstractionPattern origEltType, CanType substEltType) { + emit(tuple->getElement(substEltIndex), origEltType); + }, + [&](unsigned origEltIndex, unsigned substEltIndex, + AbstractionPattern origExpansionType, + CanTupleEltTypeArrayRef substEltTypes) { + SmallVector eltArgs; + eltArgs.reserve(substEltTypes.size()); + for (auto i : range(substEltIndex, substEltTypes.size())) { + eltArgs.emplace_back(tuple->getElement(i)); + } + emitPackArg(eltArgs, origExpansionType); + }); return; } From c032cc54087cdd7274a90b8020a88f29510c180b Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 22 Mar 2023 02:08:03 -0400 Subject: [PATCH 07/11] Add a test case for the work in this PR. My original test case here used a memberwise initializer, but those use their own logic for binding and forward parameters which will need to be updated separately. --- test/SILGen/variadic-generic-tuples.swift | 49 +++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/test/SILGen/variadic-generic-tuples.swift b/test/SILGen/variadic-generic-tuples.swift index a0fe0fad7bb03..fbcfbe6ec51e7 100644 --- a/test/SILGen/variadic-generic-tuples.swift +++ b/test/SILGen/variadic-generic-tuples.swift @@ -204,3 +204,52 @@ 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() +} + +/* We still crash with memberwise initializers +func generateConcreteMemberTuple() -> TupleHolder { + return HasMemberTuple(content: (0, "hello")) +} + */ From 0c3c62bd7311e20e378154d3f04e1afcb245e196 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 22 Mar 2023 15:38:31 -0400 Subject: [PATCH 08/11] [NFC] Rename isPackExpansion -> isOrigPackExpansion for clarity --- include/swift/SIL/AbstractionPatternGenerators.h | 4 ++-- lib/SIL/IR/AbstractionPattern.cpp | 2 +- lib/SIL/IR/SILFunctionType.cpp | 2 +- lib/SILGen/FunctionInputGenerator.h | 4 ++-- lib/SILGen/SILGenPoly.cpp | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/swift/SIL/AbstractionPatternGenerators.h b/include/swift/SIL/AbstractionPatternGenerators.h index b0cbd0624f432..99ebf5ad98774 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; } diff --git a/lib/SIL/IR/AbstractionPattern.cpp b/lib/SIL/IR/AbstractionPattern.cpp index 1896258930562..6fc6d2d34d9a1 100644 --- a/lib/SIL/IR/AbstractionPattern.cpp +++ b/lib/SIL/IR/AbstractionPattern.cpp @@ -2236,7 +2236,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 28f21ac4f49da..bea349cc85f6f 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -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; 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/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, From debc8d9ebdbf0d7ae7193e5b81303723ab59c714 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 22 Mar 2023 15:39:18 -0400 Subject: [PATCH 09/11] [NFC] Move forEachTupleElement to use a generator --- include/swift/SIL/AbstractionPattern.h | 19 +-- .../swift/SIL/AbstractionPatternGenerators.h | 123 ++++++++++++++++++ lib/SIL/IR/AbstractionPattern.cpp | 79 +++++------ lib/SIL/IR/SILFunctionType.cpp | 33 ++--- lib/SILGen/ResultPlan.cpp | 28 ++-- lib/SILGen/SILGenApply.cpp | 40 +++--- lib/SILGen/SILGenProlog.cpp | 36 ++--- lib/SILGen/SILGenStmt.cpp | 48 ++++--- 8 files changed, 251 insertions(+), 155 deletions(-) diff --git a/include/swift/SIL/AbstractionPattern.h b/include/swift/SIL/AbstractionPattern.h index 6f31004f6233e..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. /// @@ -1174,6 +1175,10 @@ class AbstractionPattern { return CXXMethod; } + bool isOpaqueTuple() const { + return getKind() == Kind::Tuple; + } + bool isOpaqueFunctionOrOpaqueDerivativeFunction() const { return (getKind() == Kind::OpaqueFunction || getKind() == Kind::OpaqueDerivativeFunction); @@ -1356,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 99ebf5ad98774..e25636e096d05 100644 --- a/include/swift/SIL/AbstractionPatternGenerators.h +++ b/include/swift/SIL/AbstractionPatternGenerators.h @@ -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 6fc6d2d34d9a1..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, @@ -2196,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)); diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index bea349cc85f6f..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 = @@ -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 = 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 5fc3d2caa5ed5..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; } @@ -3392,19 +3390,19 @@ class ArgEmitter { auto substTupleType = cast(e->getType()->getCanonicalType()); origParamType.forEachTupleElement(substTupleType, - [&](unsigned origEltIndex, unsigned substEltIndex, - AbstractionPattern origEltType, CanType substEltType) { - emit(tuple->getElement(substEltIndex), origEltType); - }, - [&](unsigned origEltIndex, unsigned substEltIndex, - AbstractionPattern origExpansionType, - CanTupleEltTypeArrayRef substEltTypes) { + [&](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(substEltIndex, substEltTypes.size())) { + for (auto i : range(elt.getSubstIndex(), substEltTypes.size())) { eltArgs.emplace_back(tuple->getElement(i)); } - emitPackArg(eltArgs, origExpansionType); + emitPackArg(eltArgs, elt.getOrigType()); }); return; } 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); From 88bae358efc69e03c1d5757f60e872612920dc84 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 22 Mar 2023 22:02:01 -0400 Subject: [PATCH 10/11] [NFC] Rename Generator::getCurrent() to Generator::get() Just for brevity's sake. --- include/swift/Basic/Generators.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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; } From 481f9c7362486026357996fb0a91ab330616ee89 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 22 Mar 2023 22:03:18 -0400 Subject: [PATCH 11/11] Fix memberwise initializers for structs with variadic-tuple fields --- lib/SILGen/SILGenConstructor.cpp | 190 ++++++++++++++++++++-- test/SILGen/variadic-generic-tuples.swift | 38 ++++- 2 files changed, 208 insertions(+), 20 deletions(-) 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/test/SILGen/variadic-generic-tuples.swift b/test/SILGen/variadic-generic-tuples.swift index fbcfbe6ec51e7..b1585c5804b98 100644 --- a/test/SILGen/variadic-generic-tuples.swift +++ b/test/SILGen/variadic-generic-tuples.swift @@ -248,8 +248,38 @@ func takesConcreteTupleHolderFactory(factory: () -> TupleHolder) { let holder = factory() } -/* We still crash with memberwise initializers -func generateConcreteMemberTuple() -> TupleHolder { - return HasMemberTuple(content: (0, "hello")) +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")) } - */