Skip to content

Commit 8db2e00

Browse files
authored
Merge pull request #67420 from slavapestov/keypath-descriptor-crash-with-variadics-5.9
Don't emit keypath descriptors for unsupported cases involving variadic generics [5.9]
2 parents 0a51b8d + 87f4f71 commit 8db2e00

File tree

6 files changed

+290
-19
lines changed

6 files changed

+290
-19
lines changed

include/swift/SIL/AbstractionPattern.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ namespace swift {
3737
namespace Lowering {
3838
class FunctionParamGenerator;
3939
class TupleElementGenerator;
40+
class PackElementGenerator;
4041

4142
/// A pattern for the abstraction of a value.
4243
///
@@ -1394,7 +1395,7 @@ class AbstractionPattern {
13941395

13951396
/// Is the given pack type a valid substitution of this abstraction
13961397
/// pattern?
1397-
bool matchesPack(CanPackType substType);
1398+
bool matchesPack(CanPackType substType) const;
13981399

13991400
bool isPack() const {
14001401
switch (getKind()) {
@@ -1450,6 +1451,26 @@ class AbstractionPattern {
14501451
llvm_unreachable("bad kind");
14511452
}
14521453

1454+
/// Perform a parallel visitation of the elements of a pack type,
1455+
/// preserving structure about where pack expansions appear in the
1456+
/// original type and how many elements of the substituted type they
1457+
/// expand to.
1458+
///
1459+
/// This pattern must be a pack pattern.
1460+
void forEachPackElement(CanPackType substPackType,
1461+
llvm::function_ref<void(PackElementGenerator &element)> fn) const;
1462+
1463+
/// Perform a parallel visitation of the elements of a pack type,
1464+
/// expanding the elements of the type. This preserves the structure
1465+
/// of the *substituted* pack type: it will be called once per element
1466+
/// of the substituted type, in order.
1467+
///
1468+
/// This pattern must match the substituted type, but it may be an
1469+
/// opaque pattern.
1470+
void forEachExpandedPackElement(CanPackType substPackType,
1471+
llvm::function_ref<void(AbstractionPattern origEltType,
1472+
CanType substEltType)> handleElement) const;
1473+
14531474
/// Given that the value being abstracted is a move only type, return the
14541475
/// abstraction pattern with the move only bit removed.
14551476
AbstractionPattern removingMoveOnlyWrapper() const;

include/swift/SIL/AbstractionPatternGenerators.h

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,122 @@ class TupleElementGenerator {
335335
}
336336
};
337337

338+
/// A generator for traversing the formal elements of a pack type
339+
/// while properly respecting variadic generics.
340+
class PackElementGenerator {
341+
// The steady state of the generator.
342+
343+
/// The abstraction pattern of the entire pack type. Set once
344+
/// during construction.
345+
AbstractionPattern origPackType;
346+
347+
/// The substituted pack type. Set once during construction.
348+
CanPackType substPackType;
349+
350+
/// The number of orig elements to traverse. Set once during
351+
/// construction.
352+
unsigned numOrigElts;
353+
354+
/// The index of the current orig element.
355+
/// Incremented during advance().
356+
unsigned origEltIndex = 0;
357+
358+
/// The (start) index of the current subst elements.
359+
/// Incremented during advance().
360+
unsigned substEltIndex = 0;
361+
362+
/// The number of subst elements corresponding to the current
363+
/// orig element.
364+
unsigned numSubstEltsForOrigElt;
365+
366+
/// Whether the current orig element is a pack expansion.
367+
bool origEltIsExpansion;
368+
369+
/// The abstraction pattern of the current orig element.
370+
/// If it is a pack expansion, this is the expansion type, not the
371+
/// pattern type.
372+
AbstractionPattern origEltType = AbstractionPattern::getInvalid();
373+
374+
/// Load the informaton for the current orig element into the
375+
/// fields above for it.
376+
void loadElement() {
377+
origEltType = origPackType.getPackElementType(origEltIndex);
378+
origEltIsExpansion = origEltType.isPackExpansion();
379+
numSubstEltsForOrigElt =
380+
(origEltIsExpansion
381+
? origEltType.getNumPackExpandedComponents()
382+
: 1);
383+
}
384+
385+
public:
386+
PackElementGenerator(AbstractionPattern origTupleType,
387+
CanPackType substPackType);
388+
389+
/// Is the traversal finished? If so, none of the getters below
390+
/// are allowed to be called.
391+
bool isFinished() const {
392+
return origEltIndex == numOrigElts;
393+
}
394+
395+
/// Advance to the next orig element.
396+
void advance() {
397+
assert(!isFinished());
398+
origEltIndex++;
399+
substEltIndex += numSubstEltsForOrigElt;
400+
if (!isFinished()) loadElement();
401+
}
402+
403+
/// Return the index of the current orig element.
404+
unsigned getOrigIndex() const {
405+
assert(!isFinished());
406+
return origEltIndex;
407+
}
408+
409+
/// Return the index of the (first) subst element corresponding
410+
/// to the current orig element.
411+
unsigned getSubstIndex() const {
412+
assert(!isFinished());
413+
return substEltIndex;
414+
}
415+
416+
IntRange<unsigned> getSubstIndexRange() const {
417+
assert(!isFinished());
418+
return IntRange<unsigned>(substEltIndex,
419+
substEltIndex + numSubstEltsForOrigElt);
420+
}
421+
422+
/// Return the type of the current orig element.
423+
const AbstractionPattern &getOrigType() const {
424+
assert(!isFinished());
425+
return origEltType;
426+
}
427+
428+
/// Return whether the current orig element type is a pack expansion.
429+
bool isOrigPackExpansion() const {
430+
assert(!isFinished());
431+
return origEltIsExpansion;
432+
}
433+
434+
/// Return the substituted elements corresponding to the current
435+
/// orig element type. If the current orig element is not a
436+
/// pack expansion, this will have exactly one element.
437+
CanTypeArrayRef getSubstTypes() const {
438+
assert(!isFinished());
439+
return substPackType
440+
.getElementTypes().slice(substEltIndex,
441+
numSubstEltsForOrigElt);
442+
}
443+
444+
/// Call this to finalize the traversal and assert that it was done
445+
/// properly.
446+
void finish() {
447+
assert(isFinished() && "didn't finish the traversal");
448+
assert(substEltIndex == substPackType->getNumElements() &&
449+
"didn't exhaust subst elements; possible missing subs on "
450+
"orig pack type");
451+
}
452+
};
453+
338454
} // end namespace Lowering
339455
} // end namespace swift
340456

lib/SIL/IR/AbstractionPattern.cpp

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -707,7 +707,7 @@ AbstractionPattern::getPackElementType(unsigned index) const {
707707
llvm_unreachable("bad kind");
708708
}
709709

710-
bool AbstractionPattern::matchesPack(CanPackType substType) {
710+
bool AbstractionPattern::matchesPack(CanPackType substType) const {
711711
switch (getKind()) {
712712
case Kind::Invalid:
713713
llvm_unreachable("querying invalid abstraction pattern!");
@@ -741,6 +741,72 @@ bool AbstractionPattern::matchesPack(CanPackType substType) {
741741
llvm_unreachable("bad kind");
742742
}
743743

744+
void AbstractionPattern::forEachPackElement(CanPackType substType,
745+
llvm::function_ref<void(PackElementGenerator &)> handleElement) const {
746+
PackElementGenerator elt(*this, substType);
747+
for (; !elt.isFinished(); elt.advance()) {
748+
handleElement(elt);
749+
}
750+
elt.finish();
751+
}
752+
753+
void AbstractionPattern::forEachExpandedPackElement(CanPackType substPackType,
754+
llvm::function_ref<void(AbstractionPattern origEltType,
755+
CanType substEltType)>
756+
handleElement) const {
757+
assert(matchesPack(substPackType));
758+
759+
auto substEltTypes = substPackType.getElementTypes();
760+
761+
// Handle opaque patterns by just iterating the substituted components.
762+
if (!isPack()) {
763+
for (auto i : indices(substEltTypes)) {
764+
handleElement(getPackElementType(i), substEltTypes[i]);
765+
}
766+
return;
767+
}
768+
769+
// For non-opaque patterns, we have to iterate the original components
770+
// in order to match things up properly, but we'll still end up calling
771+
// once per substituted element.
772+
size_t substEltIndex = 0;
773+
for (size_t origEltIndex : range(getNumPackElements())) {
774+
auto origEltType = getPackElementType(origEltIndex);
775+
if (!origEltType.isPackExpansion()) {
776+
handleElement(origEltType, substEltTypes[substEltIndex]);
777+
substEltIndex++;
778+
} else {
779+
auto origPatternType = origEltType.getPackExpansionPatternType();
780+
for (auto i : range(origEltType.getNumPackExpandedComponents())) {
781+
(void) i;
782+
auto substEltType = substEltTypes[substEltIndex];
783+
// When the substituted type is a pack expansion, pass down
784+
// the original element type so that it's *also* a pack expansion.
785+
// Clients expect to look through this structure in parallel on
786+
// both types. The count is misleading, but normal usage won't
787+
// access it, and there's nothing we could provide that *wouldn't*
788+
// be misleading in one way or another.
789+
handleElement(isa<PackExpansionType>(substEltType)
790+
? origEltType : origPatternType,
791+
substEltType);
792+
substEltIndex++;
793+
}
794+
}
795+
}
796+
assert(substEltIndex == substEltTypes.size());
797+
}
798+
799+
PackElementGenerator::PackElementGenerator(
800+
AbstractionPattern origPackType,
801+
CanPackType substPackType)
802+
: origPackType(origPackType), substPackType(substPackType) {
803+
assert(origPackType.isPack());
804+
assert(origPackType.matchesPack(substPackType));
805+
numOrigElts = origPackType.getNumPackElements();
806+
807+
if (!isFinished()) loadElement();
808+
}
809+
744810
AbstractionPattern
745811
AbstractionPattern::getPackExpansionComponentType(CanType substType) const {
746812
return getPackExpansionComponentType(isa<PackExpansionType>(substType));
@@ -2257,12 +2323,25 @@ class SubstFunctionTypePatternVisitor
22572323
}
22582324

22592325
CanType visitPackType(CanPackType pack, AbstractionPattern pattern) {
2326+
assert(pattern.isPack());
2327+
22602328
// Break down the pack.
22612329
SmallVector<CanType, 4> packElts;
2262-
for (auto i : range(pack->getNumElements())) {
2263-
packElts.push_back(visit(pack.getElementType(i),
2264-
pattern.getPackElementType(i)));
2265-
}
2330+
2331+
pattern.forEachPackElement(pack, [&](PackElementGenerator &elt) {
2332+
auto substEltTypes = elt.getSubstTypes();
2333+
CanType eltTy;
2334+
if (!elt.isOrigPackExpansion()) {
2335+
eltTy = visit(substEltTypes[0], elt.getOrigType());
2336+
} else {
2337+
CanType candidateSubstType;
2338+
if (!substEltTypes.empty())
2339+
candidateSubstType = substEltTypes[0];
2340+
eltTy = handlePackExpansion(elt.getOrigType(), candidateSubstType);
2341+
}
2342+
2343+
packElts.push_back(eltTy);
2344+
});
22662345

22672346
return CanPackType::get(TC.Context, packElts);
22682347
}

lib/SIL/IR/SIL.cpp

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -296,17 +296,53 @@ bool SILModule::isTypeMetadataForLayoutAccessible(SILType type) {
296296
return ::isTypeMetadataForLayoutAccessible(*this, type);
297297
}
298298

299-
bool AbstractStorageDecl::exportsPropertyDescriptor() const {
300-
// The storage needs a descriptor if it sits at a module's ABI boundary,
301-
// meaning it has public linkage.
302-
299+
static bool isUnsupportedKeyPathValueType(Type ty) {
300+
// Visit lowered positions.
301+
if (auto tupleTy = ty->getAs<TupleType>()) {
302+
for (auto eltTy : tupleTy->getElementTypes()) {
303+
if (eltTy->is<PackExpansionType>())
304+
return true;
305+
306+
if (isUnsupportedKeyPathValueType(eltTy))
307+
return true;
308+
}
309+
310+
return false;
311+
}
312+
313+
if (auto objTy = ty->getOptionalObjectType())
314+
ty = objTy;
315+
316+
// FIXME: Remove this once isUnimplementableVariadicFunctionAbstraction()
317+
// goes away in SILGenPoly.cpp.
318+
if (auto funcTy = ty->getAs<FunctionType>()) {
319+
for (auto param : funcTy->getParams()) {
320+
auto paramTy = param.getPlainType();
321+
if (paramTy->is<PackExpansionType>())
322+
return true;
323+
324+
if (isUnsupportedKeyPathValueType(paramTy))
325+
return true;
326+
}
327+
328+
if (isUnsupportedKeyPathValueType(funcTy->getResult()))
329+
return true;
330+
}
331+
303332
// Noncopyable types aren't supported by key paths in their current form.
304333
// They would also need a new ABI that's yet to be implemented in order to
305334
// be properly supported, so let's suppress the descriptor for now if either
306335
// the container or storage type of the declaration is non-copyable.
307-
if (getValueInterfaceType()->isPureMoveOnly()) {
308-
return false;
309-
}
336+
if (ty->isPureMoveOnly())
337+
return true;
338+
339+
return false;
340+
}
341+
342+
bool AbstractStorageDecl::exportsPropertyDescriptor() const {
343+
// The storage needs a descriptor if it sits at a module's ABI boundary,
344+
// meaning it has public linkage.
345+
310346
if (!isStatic()) {
311347
if (auto contextTy = getDeclContext()->getDeclaredTypeInContext()) {
312348
if (contextTy->isPureMoveOnly()) {
@@ -362,6 +398,10 @@ bool AbstractStorageDecl::exportsPropertyDescriptor() const {
362398
llvm_unreachable("should be definition linkage?");
363399
}
364400

401+
if (isUnsupportedKeyPathValueType(getValueInterfaceType())) {
402+
return false;
403+
}
404+
365405
// Subscripts with inout arguments (FIXME)and reabstracted arguments(/FIXME)
366406
// don't have descriptors either.
367407
if (auto sub = dyn_cast<SubscriptDecl>(this)) {

lib/SIL/IR/TypeLowering.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2609,14 +2609,13 @@ static CanSILPackType computeLoweredPackType(TypeConverter &tc,
26092609
SmallVector<CanType, 4> loweredElts;
26102610
loweredElts.reserve(substType->getNumElements());
26112611

2612-
for (auto i : indices(substType->getElementTypes())) {
2613-
auto origEltType = origType.getPackElementType(i);
2614-
auto substEltType = substType.getElementType(i);
2615-
2616-
CanType loweredTy =
2612+
origType.forEachExpandedPackElement(substType,
2613+
[&](AbstractionPattern origEltType,
2614+
CanType substEltType) {
2615+
auto loweredTy =
26172616
tc.getLoweredRValueType(context, origEltType, substEltType);
26182617
loweredElts.push_back(loweredTy);
2619-
}
2618+
});
26202619

26212620
bool elementIsAddress = true; // TODO
26222621
SILPackType::ExtInfo extInfo(elementIsAddress);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %target-swift-emit-silgen -enable-library-evolution -disable-availability-checking %s | %FileCheck %s
2+
3+
// rdar://problem/112474421
4+
5+
public struct G<each T> {
6+
public var property1: (repeat each T) -> ()
7+
public var property2: ((repeat each T)) -> ()
8+
public var property3: (G<repeat each T>) -> ()
9+
}
10+
11+
// We don't attempt to emit a keypath descriptor for property1 and property2,
12+
// and we shouldn't crash while emitting a keypath descriptor for property3.
13+
14+
// CHECK-NOT: sil_property #G.property1<each τ_0_0>
15+
// CHECK-NOT: sil_property #G.property2<each τ_0_0>
16+
// CHECK-LABEL: sil_property #G.property3<each τ_0_0>

0 commit comments

Comments
 (0)