Skip to content

[5.9] A few SILGen fixes for variadic generics #66684

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions include/swift/SIL/AbstractionPatternGenerators.h
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,22 @@ class TupleElementGenerator {
}
}

/// Like `getSubstTypes`, but uses a different and possibly
/// non-canonical tuple type.
TupleEltTypeArrayRef getSubstTypes(Type ncSubstType) const {
assert(!isFinished());
if (!origTupleVanishes) {
return ncSubstType->castTo<TupleType>()
->getElementTypes().slice(substEltIndex,
numSubstEltsForOrigElt);
} else if (numSubstEltsForOrigElt == 0) {
return TupleEltTypeArrayRef();
} else {
scratchSubstElt = TupleTypeElt(ncSubstType);
return TupleEltTypeArrayRef(scratchSubstElt);
}
}

/// Call this to finalize the traversal and assert that it was done
/// properly.
void finish() {
Expand Down
11 changes: 9 additions & 2 deletions lib/SIL/IR/AbstractionPattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2064,6 +2064,9 @@ class SubstFunctionTypePatternVisitor
if (auto gp = handleTypeParameter(pattern, t, level))
return gp;

if (pattern.isTuple())
return visitTuplePattern(t, pattern);

return CanTypeVisitor::visit(t, pattern);
}

Expand Down Expand Up @@ -2312,11 +2315,15 @@ class SubstFunctionTypePatternVisitor
TC.Context, ppt->getBaseType(), substArgs));
}

CanType visitTupleType(CanTupleType tuple, AbstractionPattern pattern) {
/// Visit a tuple pattern. Note that, because of vanishing tuples,
/// we can't handle this case by matching a tuple type in the
/// substituted type; we have to check for a tuple pattern in the
/// top-level visit routine.
CanType visitTuplePattern(CanType substType, AbstractionPattern pattern) {
assert(pattern.isTuple());

SmallVector<TupleTypeElt, 4> tupleElts;
pattern.forEachTupleElement(tuple, [&](TupleElementGenerator &elt) {
pattern.forEachTupleElement(substType, [&](TupleElementGenerator &elt) {
auto substEltTypes = elt.getSubstTypes();
CanType eltTy;
if (!elt.isOrigPackExpansion()) {
Expand Down
10 changes: 10 additions & 0 deletions lib/SIL/IR/SILType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,16 @@ bool SILType::isLoweringOf(TypeExpansionContext context, SILModule &Mod,
}
}

// The pattern of a pack expansion is lowered.
if (auto formalExpansion = dyn_cast<PackExpansionType>(formalType)) {
if (auto loweredExpansion = loweredType.getAs<PackExpansionType>()) {
return loweredExpansion.getCountType() == formalExpansion.getCountType()
&& SILType::getPrimitiveAddressType(loweredExpansion.getPatternType())
.isLoweringOf(context, Mod, formalExpansion.getPatternType());
}
return false;
}

// Dynamic self has the same lowering as its contained type.
if (auto dynamicSelf = dyn_cast<DynamicSelfType>(formalType))
formalType = dynamicSelf.getSelfType();
Expand Down
8 changes: 2 additions & 6 deletions lib/SILGen/RValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,8 @@ class ExplodeTupleValue

void visitType(CanType formalType, ManagedValue v) {
// If we have a loadable type that has not been loaded, actually load it.
if (!v.getType().isObject() && v.getType().isLoadable(SGF.F)) {
if (v.isPlusOne(SGF)) {
v = SGF.B.createLoadTake(loc, v);
} else {
v = SGF.B.createLoadBorrow(loc, v);
}
if (!v.getType().isObject()) {
v = SGF.B.createLoadIfLoadable(loc, v);
}

values.push_back(v);
Expand Down
17 changes: 17 additions & 0 deletions lib/SILGen/SILGenBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,23 @@ ManagedValue SILGenBuilder::createUncheckedTakeEnumDataAddr(
return cloner.clone(result);
}

ManagedValue
SILGenBuilder::createLoadIfLoadable(SILLocation loc, ManagedValue addr) {
assert(addr.getType().isAddress());
if (!addr.getType().isLoadable(SGF.F))
return addr;
return createLoadWithSameOwnership(loc, addr);
}

ManagedValue
SILGenBuilder::createLoadWithSameOwnership(SILLocation loc,
ManagedValue addr) {
if (addr.isPlusOne(SGF))
return createLoadTake(loc, addr);
else
return createLoadBorrow(loc, addr);
}

ManagedValue SILGenBuilder::createLoadTake(SILLocation loc, ManagedValue v) {
auto &lowering = SGF.getTypeLowering(v.getType());
return createLoadTake(loc, v, lowering);
Expand Down
9 changes: 9 additions & 0 deletions lib/SILGen/SILGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,15 @@ class SILGenBuilder : public SILBuilder {
ManagedValue createUncheckedTakeEnumDataAddr(SILLocation loc, ManagedValue operand,
EnumElementDecl *element, SILType ty);

/// Given the address of a value, load a scalar value from it if the type
/// is loadable. Most general routines in SILGen expect to work with
/// values with the canonical scalar-ness for their type.
ManagedValue createLoadIfLoadable(SILLocation loc, ManagedValue addr);

/// Given the address of a loadable value, load the value but don't
/// change the ownership.
ManagedValue createLoadWithSameOwnership(SILLocation loc, ManagedValue addr);

ManagedValue createLoadTake(SILLocation loc, ManagedValue addr);
ManagedValue createLoadTake(SILLocation loc, ManagedValue addr,
const TypeLowering &lowering);
Expand Down
16 changes: 7 additions & 9 deletions lib/SILGen/SILGenConstructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ static RValue emitImplicitValueConstructorArg(SILGenFunction &SGF,
auto eltAddr =
SGF.B.createPackElementGet(loc, packIndex, arg, eltTy);
ManagedValue eltMV = emitManagedParameter(SGF, eltAddr, argIsConsumed);
eltMV = SGF.B.createLoadIfLoadable(loc, eltMV);
eltInit->copyOrInitValueInto(SGF, loc, eltMV, argIsConsumed);
eltInit->finishInitialization(SGF);
});
Expand All @@ -232,21 +233,18 @@ static RValue emitImplicitValueConstructorArg(SILGenFunction &SGF,

ManagedValue mvArg = emitManagedParameter(SGF, arg, argIsConsumed);

// This can happen if the value is resilient in the calling convention
// but not resilient locally.
if (argType.isAddress()) {
mvArg = SGF.B.createLoadIfLoadable(loc, mvArg);
}

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
// but not resilient locally.
if (argType.isLoadable(SGF.F) && argType.isAddress()) {
if (mvArg.isPlusOne(SGF))
mvArg = SGF.B.createLoadTake(loc, mvArg);
else
mvArg = SGF.B.createLoadBorrow(loc, mvArg);
}

return RValue(SGF, loc, type, mvArg);
}

Expand Down
94 changes: 64 additions & 30 deletions lib/SILGen/SILGenProlog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,7 @@ class EmitBBArguments : public CanTypeVisitor<EmitBBArguments,
bool argIsLoadable = argType.isLoadable(SGF.F);
if (argIsLoadable) {
if (argType.isAddress()) {
if (mv.isPlusOne(SGF))
mv = SGF.B.createLoadTake(loc, mv);
else
mv = SGF.B.createLoadBorrow(loc, mv);
mv = SGF.B.createLoadWithSameOwnership(loc, mv);
argType = argType.getObjectType();
}
}
Expand Down Expand Up @@ -1569,54 +1566,91 @@ SILValue SILGenFunction::emitGetCurrentExecutor(SILLocation loc) {
return ExpectedExecutor;
}

static void emitIndirectPackParameter(SILGenFunction &SGF,
PackType *resultType,
CanTupleEltTypeArrayRef
resultTypesInContext,
AbstractionPattern origExpansionType,
DeclContext *DC) {
auto &ctx = SGF.getASTContext();

bool indirect =
origExpansionType.arePackElementsPassedIndirectly(SGF.SGM.Types);
SmallVector<CanType, 4> packElts;
for (auto substEltType : resultTypesInContext) {
auto origComponentType
= origExpansionType.getPackExpansionComponentType(substEltType);
CanType loweredEltTy =
SGF.getLoweredRValueType(origComponentType, substEltType);
packElts.push_back(loweredEltTy);
}

SILPackType::ExtInfo extInfo(indirect);
auto packType = SILPackType::get(ctx, extInfo, packElts);
auto resultSILType = SILType::getPrimitiveAddressType(packType);

auto var = new (ctx) ParamDecl(SourceLoc(), SourceLoc(),
ctx.getIdentifier("$return_value"), SourceLoc(),
ctx.getIdentifier("$return_value"),
DC);
var->setSpecifier(ParamSpecifier::InOut);
var->setInterfaceType(resultType);
auto *arg = SGF.F.begin()->createFunctionArgument(resultSILType, var);
(void)arg;
}

static void emitIndirectResultParameters(SILGenFunction &SGF,
Type resultType,
AbstractionPattern origResultType,
DeclContext *DC) {
// Expand tuples.
CanType resultTypeInContext =
DC->mapTypeIntoContext(resultType)->getCanonicalType();

// Tuples in the original result type are expanded.
if (origResultType.isTuple()) {
auto tupleType = resultType->castTo<TupleType>();
for (unsigned i = 0, e = origResultType.getNumTupleElements(); i < e; ++i) {
emitIndirectResultParameters(SGF, tupleType->getElementType(i),
origResultType.getTupleElementType(i),
DC);
}
origResultType.forEachTupleElement(resultTypeInContext,
[&](TupleElementGenerator &elt) {
auto origEltType = elt.getOrigType();
auto substEltTypes = elt.getSubstTypes(resultType);

// If the original element isn't a pack expansion, pull out the
// corresponding substituted tuple element and recurse.
if (!elt.isOrigPackExpansion()) {
emitIndirectResultParameters(SGF, substEltTypes[0], origEltType, DC);
return;
}

// Otherwise, bind a pack parameter.
PackType *resultPackType = [&] {
SmallVector<Type, 4> packElts(substEltTypes.begin(),
substEltTypes.end());
return PackType::get(SGF.getASTContext(), packElts);
}();
emitIndirectPackParameter(SGF, resultPackType, elt.getSubstTypes(),
origEltType, DC);
});
return;
}

assert(!resultType->is<PackExpansionType>());

// If the return type is address-only, emit the indirect return argument.
auto &resultTI =
SGF.SGM.Types.getTypeLowering(origResultType,
DC->mapTypeIntoContext(resultType),
SGF.SGM.Types.getTypeLowering(origResultType, resultTypeInContext,
SGF.getTypeExpansionContext());

// The calling convention always uses minimal resilience expansion.
auto &resultTIConv = SGF.SGM.Types.getTypeLowering(
DC->mapTypeIntoContext(resultType), TypeExpansionContext::minimal());
resultTypeInContext, TypeExpansionContext::minimal());
auto resultConvType = resultTIConv.getLoweredType();

auto &ctx = SGF.getASTContext();

SILType resultSILType = resultTI.getLoweredType().getAddressType();

// FIXME: respect susbtitution properly and collect the appropriate
// tuple components from resultType that correspond to the
// pack expansion in origType.
bool isPackExpansion = resultType->is<PackExpansionType>();
if (isPackExpansion) {
resultType = PackType::get(ctx, {resultType});

bool indirect =
origResultType.arePackElementsPassedIndirectly(SGF.SGM.Types);
SILPackType::ExtInfo extInfo(indirect);
resultSILType = SILType::getPrimitiveAddressType(
SILPackType::get(ctx, extInfo, {resultSILType.getASTType()}));
}

// And the abstraction pattern may force an indirect return even if the
// concrete type wouldn't normally be returned indirectly.
if (!isPackExpansion &&
!SILModuleConventions::isReturnedIndirectlyInSIL(resultConvType,
if (!SILModuleConventions::isReturnedIndirectlyInSIL(resultConvType,
SGF.SGM.M)) {
if (!SILModuleConventions(SGF.SGM.M).useLoweredAddresses()
|| origResultType.getResultConvention(SGF.SGM.Types) != AbstractionPattern::Indirect)
Expand Down
38 changes: 31 additions & 7 deletions lib/SILGen/SILGenStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -580,11 +580,29 @@ prepareIndirectResultInit(SILGenFunction &SGF, SILLocation loc,
SmallVectorImpl<CleanupHandle> &cleanups) {
// Recursively decompose tuple abstraction patterns.
if (origResultType.isTuple()) {
auto resultTupleType = cast<TupleType>(resultType);
auto tupleInit = new TupleInitialization(resultTupleType);
tupleInit->SubInitializations.reserve(resultTupleType->getNumElements());
// Normally, we build a compound initialization for the tuple. But
// the initialization we build should match the substituted type,
// so if the tuple in the abstraction pattern vanishes under variadic
// substitution, we actually just want to return the initializer
// for the surviving component.
TupleInitialization *tupleInit = nullptr;
SmallVector<InitializationPtr, 1> singletonEltInit;

bool vanishes =
origResultType.getVanishingTupleElementPatternType().hasValue();
if (!vanishes) {
auto resultTupleType = cast<TupleType>(resultType);
tupleInit = new TupleInitialization(resultTupleType);
tupleInit->SubInitializations.reserve(
cast<TupleType>(resultType)->getNumElements());
}

// The list of element initializers to build into.
auto &eltInits = (vanishes
? static_cast<SmallVectorImpl<InitializationPtr> &>(singletonEltInit)
: tupleInit->SubInitializations);

origResultType.forEachTupleElement(resultTupleType,
origResultType.forEachTupleElement(resultType,
[&](TupleElementGenerator &elt) {
if (!elt.isOrigPackExpansion()) {
auto eltInit = prepareIndirectResultInit(SGF, loc, fnTypeForResults,
Expand All @@ -594,7 +612,7 @@ prepareIndirectResultInit(SILGenFunction &SGF, SILLocation loc,
directResults,
indirectResultAddrs,
cleanups);
tupleInit->SubInitializations.push_back(std::move(eltInit));
eltInits.push_back(std::move(eltInit));
} else {
assert(allResults[0].isPack());
assert(SGF.silConv.isSILIndirect(allResults[0]));
Expand All @@ -604,11 +622,17 @@ prepareIndirectResultInit(SILGenFunction &SGF, SILLocation loc,
indirectResultAddrs = indirectResultAddrs.slice(1);

preparePackResultInit(SGF, loc, elt.getOrigType(), elt.getSubstTypes(),
packAddr,
cleanups, tupleInit->SubInitializations);
packAddr, cleanups, eltInits);
}
});

if (vanishes) {
assert(singletonEltInit.size() == 1);
return std::move(singletonEltInit.front());
}

assert(tupleInit);
assert(eltInits.size() == cast<TupleType>(resultType)->getNumElements());
return InitializationPtr(tupleInit);
}

Expand Down
9 changes: 9 additions & 0 deletions test/SILGen/variadic-generic-closures.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,12 @@ public struct UsesG<each Input> {
public init<E>(builder: (repeat G<each Input>) -> E) {}
}
UsesG<Int, String, Bool> { a, b, c in 0 }

// rdar://107367324
func returnVariadicClosure<each T, R>(f: @escaping (repeat each T) -> R) -> (repeat each T) -> R {
{ (t: repeat each T) -> R in
let tuple = (repeat each t)
print(tuple)
return f(repeat each t)
}
}
Loading