Skip to content

Commit 10661df

Browse files
authored
Merge pull request #8958 from slavapestov/subclass-existentials-runtime
More runtime support for subclass existentials
2 parents cfb0827 + e0391ca commit 10661df

17 files changed

+828
-60
lines changed

include/swift/ABI/MetadataValues.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,8 @@ class ExistentialTypeFlags {
404404
enum : int_type {
405405
NumWitnessTablesMask = 0x00FFFFFFU,
406406
ClassConstraintMask = 0x80000000U,
407-
SpecialProtocolMask = 0x7F000000U,
407+
HasSuperclassMask = 0x40000000U,
408+
SpecialProtocolMask = 0x3F000000U,
408409
SpecialProtocolShift = 24U,
409410
};
410411
int_type Data;
@@ -421,6 +422,11 @@ class ExistentialTypeFlags {
421422
| (bool(c) ? ClassConstraintMask : 0));
422423
}
423424
constexpr ExistentialTypeFlags
425+
withHasSuperclass(bool hasSuperclass) const {
426+
return ExistentialTypeFlags((Data & ~HasSuperclassMask)
427+
| (hasSuperclass ? HasSuperclassMask : 0));
428+
}
429+
constexpr ExistentialTypeFlags
424430
withSpecialProtocol(SpecialProtocol sp) const {
425431
return ExistentialTypeFlags((Data & ~SpecialProtocolMask)
426432
| (int_type(sp) << SpecialProtocolShift));
@@ -433,7 +439,11 @@ class ExistentialTypeFlags {
433439
ProtocolClassConstraint getClassConstraint() const {
434440
return ProtocolClassConstraint(bool(Data & ClassConstraintMask));
435441
}
436-
442+
443+
bool hasSuperclassConstraint() const {
444+
return bool(Data & HasSuperclassMask);
445+
}
446+
437447
/// Return whether this existential type represents an uncomposed special
438448
/// protocol.
439449
SpecialProtocol getSpecialProtocol() const {

include/swift/Runtime/Metadata.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2445,9 +2445,16 @@ struct TargetExistentialTypeMetadata : public TargetMetadata<Runtime> {
24452445
return Flags.getClassConstraint() == ProtocolClassConstraint::Class;
24462446
}
24472447

2448-
const Metadata *getSuperclassConstraint() const {
2449-
// FIXME
2450-
return nullptr;
2448+
const TargetMetadata<Runtime> *getSuperclassConstraint() const {
2449+
if (!Flags.hasSuperclassConstraint())
2450+
return nullptr;
2451+
2452+
// Get a pointer to tail-allocated storage for this metadata record.
2453+
auto Pointer = reinterpret_cast<
2454+
ConstTargetMetadataPointer<Runtime, TargetMetadata> const *>(this + 1);
2455+
2456+
// The superclass immediately follows the list of protocol descriptors.
2457+
return Pointer[Protocols.NumProtocols];
24512458
}
24522459

24532460
static bool classof(const TargetMetadata<Runtime> *metadata) {

lib/IRGen/GenCast.cpp

Lines changed: 88 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -291,21 +291,34 @@ llvm::Value *irgen::emitReferenceToObjCProtocol(IRGenFunction &IGF,
291291
/// Emit a helper function to look up \c numProtocols witness tables given
292292
/// a value and a type metadata reference.
293293
///
294-
/// The function's input type is (value, metadataValue, protocol...)
294+
/// If \p checkClassConstraint is true, we must emit an explicit check that the
295+
/// instance is a class.
296+
///
297+
/// If \p checkSuperclassConstraint is true, we are given an additional parameter
298+
/// with a superclass type in it, and must emit a check that the instance is a
299+
/// subclass of the given class.
300+
///
301+
/// The function's input type is (value, metadataValue, superclass?, protocol...)
295302
/// The function's output type is (value, witnessTable...)
296303
///
297304
/// The value is NULL if the cast failed.
298-
static llvm::Function *emitExistentialScalarCastFn(IRGenModule &IGM,
299-
unsigned numProtocols,
300-
CheckedCastMode mode,
301-
bool checkClassConstraint) {
305+
static llvm::Function *
306+
emitExistentialScalarCastFn(IRGenModule &IGM,
307+
unsigned numProtocols,
308+
CheckedCastMode mode,
309+
bool checkClassConstraint,
310+
bool checkSuperclassConstraint) {
311+
assert(!checkSuperclassConstraint || checkClassConstraint);
312+
302313
// Build the function name.
303314
llvm::SmallString<32> name;
304315
{
305316
llvm::raw_svector_ostream os(name);
306317
os << "dynamic_cast_existential_";
307318
os << numProtocols;
308-
if (checkClassConstraint)
319+
if (checkSuperclassConstraint)
320+
os << "_superclass";
321+
else if (checkClassConstraint)
309322
os << "_class";
310323
switch (mode) {
311324
case CheckedCastMode::Unconditional:
@@ -329,6 +342,8 @@ static llvm::Function *emitExistentialScalarCastFn(IRGenModule &IGM,
329342
argTys.push_back(IGM.Int8PtrTy);
330343
argTys.push_back(IGM.TypeMetadataPtrTy);
331344
returnTys.push_back(IGM.Int8PtrTy);
345+
if (checkSuperclassConstraint)
346+
argTys.push_back(IGM.TypeMetadataPtrTy);
332347
for (unsigned i = 0; i < numProtocols; ++i) {
333348
argTys.push_back(IGM.ProtocolDescriptorPtrTy);
334349
returnTys.push_back(IGM.WitnessTablePtrTy);
@@ -355,7 +370,26 @@ static llvm::Function *emitExistentialScalarCastFn(IRGenModule &IGM,
355370
rets.add(value);
356371

357372
// Check the class constraint if necessary.
358-
if (checkClassConstraint) {
373+
if (checkSuperclassConstraint) {
374+
auto superclassMetadata = args.claimNext();
375+
auto castFn = IGF.IGM.getDynamicCastMetatypeFn();
376+
auto castResult = IGF.Builder.CreateCall(castFn, {ref,
377+
superclassMetadata});
378+
379+
auto cc = cast<llvm::Function>(castFn)->getCallingConv();
380+
381+
// FIXME: Eventually, we may want to throw.
382+
castResult->setCallingConv(cc);
383+
castResult->setDoesNotThrow();
384+
385+
auto isClass = IGF.Builder.CreateICmpNE(
386+
castResult,
387+
llvm::ConstantPointerNull::get(IGF.IGM.TypeMetadataPtrTy));
388+
389+
auto contBB = IGF.createBasicBlock("cont");
390+
IGF.Builder.CreateCondBr(isClass, contBB, failBB);
391+
IGF.Builder.emitBlock(contBB);
392+
} else if (checkClassConstraint) {
359393
auto isClass = IGF.Builder.CreateCall(IGM.getIsClassTypeFn(), ref);
360394
auto contBB = IGF.createBasicBlock("cont");
361395
IGF.Builder.CreateCondBr(isClass, contBB, failBB);
@@ -374,7 +408,7 @@ static llvm::Function *emitExistentialScalarCastFn(IRGenModule &IGM,
374408
IGF.Builder.emitBlock(contBB);
375409
rets.add(witness);
376410
}
377-
411+
378412
// If we succeeded, return the witnesses.
379413
IGF.emitScalarReturn(returnTy, rets);
380414

@@ -472,11 +506,15 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
472506
CheckedCastMode mode,
473507
Optional<MetatypeRepresentation> metatypeKind,
474508
Explosion &ex) {
475-
auto instanceType = destType.getSwiftRValueType();
476-
while (auto metatypeType = dyn_cast<ExistentialMetatypeType>(instanceType))
477-
instanceType = metatypeType.getInstanceType();
509+
auto srcInstanceType = srcType.getSwiftRValueType();
510+
auto destInstanceType = destType.getSwiftRValueType();
511+
while (auto metatypeType = dyn_cast<ExistentialMetatypeType>(
512+
destInstanceType)) {
513+
destInstanceType = metatypeType.getInstanceType();
514+
srcInstanceType = cast<AnyMetatypeType>(srcInstanceType).getInstanceType();
515+
}
478516

479-
auto layout = instanceType.getExistentialLayout();
517+
auto layout = destInstanceType.getExistentialLayout();
480518

481519
// Look up witness tables for the protocols that need them and get
482520
// references to the ObjC Protocol* values for the objc protocols.
@@ -486,7 +524,7 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
486524
bool hasClassConstraint = layout.requiresClass;
487525
bool hasClassConstraintByProtocol = false;
488526

489-
assert(!layout.superclass && "Subclass existentials not supported yet");
527+
bool hasSuperclassConstraint = bool(layout.superclass);
490528

491529
for (auto protoTy : layout.getProtocols()) {
492530
auto *protoDecl = protoTy->getDecl();
@@ -532,10 +570,34 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
532570
auto schema = IGF.getTypeInfo(destType).getSchema();
533571
resultType = schema[0].getScalarType();
534572
}
535-
// We only need to check the class constraint for metatype casts where
536-
// no protocol conformance indirectly requires the constraint for us.
537-
bool checkClassConstraint =
538-
(bool)metatypeKind && hasClassConstraint && !hasClassConstraintByProtocol;
573+
574+
// The source of a scalar cast is statically known to be a class or a
575+
// metatype, so we only have to check the class constraint in two cases:
576+
//
577+
// 1) The destination type has an explicit superclass constraint that is
578+
// more derived than what the source type is known to be.
579+
//
580+
// 2) We are casting between metatypes, in which case the source might
581+
// be a non-class metatype.
582+
bool checkClassConstraint = false;
583+
if ((bool)metatypeKind &&
584+
hasClassConstraint &&
585+
!hasClassConstraintByProtocol &&
586+
!srcInstanceType->mayHaveSuperclass())
587+
checkClassConstraint = true;
588+
589+
// If the source has an equal or more derived superclass constraint than
590+
// the destination, we can elide the superclass check.
591+
//
592+
// Note that destInstanceType is always an existential type, so calling
593+
// getSuperclass() returns the superclass constraint of the existential,
594+
// not the superclass of some concrete class.
595+
bool checkSuperclassConstraint =
596+
hasSuperclassConstraint &&
597+
!destInstanceType->getSuperclass()->isExactSuperclassOf(srcInstanceType);
598+
599+
if (checkSuperclassConstraint)
600+
checkClassConstraint = true;
539601

540602
llvm::Value *resultValue = value;
541603

@@ -671,8 +733,11 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
671733
}
672734

673735
// Look up witness tables for the protocols that need them.
674-
auto fn = emitExistentialScalarCastFn(IGF.IGM, witnessTableProtos.size(),
675-
mode, checkClassConstraint);
736+
auto fn = emitExistentialScalarCastFn(IGF.IGM,
737+
witnessTableProtos.size(),
738+
mode,
739+
checkClassConstraint,
740+
checkSuperclassConstraint);
676741

677742
llvm::SmallVector<llvm::Value *, 4> args;
678743

@@ -681,6 +746,10 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
681746
args.push_back(resultValue);
682747

683748
args.push_back(metadataValue);
749+
750+
if (checkSuperclassConstraint)
751+
args.push_back(IGF.emitTypeMetadataRef(CanType(layout.superclass)));
752+
684753
for (auto proto : witnessTableProtos)
685754
args.push_back(proto);
686755

lib/IRGen/GenReflection.cpp

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,7 @@ class CaptureDescriptorBuilder : public ReflectionMetadataBuilder {
601601
CanSILFunctionType SubstCalleeType;
602602
SubstitutionList Subs;
603603
const HeapLayout &Layout;
604+
604605
public:
605606
CaptureDescriptorBuilder(IRGenModule &IGM,
606607
SILFunction &Caller,
@@ -630,6 +631,35 @@ class CaptureDescriptorBuilder : public ReflectionMetadataBuilder {
630631
}
631632
}
632633

634+
/// Give up if we captured an opened existential type. Eventually we
635+
/// should figure out how to represent this.
636+
static bool hasOpenedExistential(CanSILFunctionType OrigCalleeType,
637+
const HeapLayout &Layout) {
638+
if (!OrigCalleeType->isPolymorphic() ||
639+
OrigCalleeType->isPseudogeneric())
640+
return false;
641+
642+
auto &Bindings = Layout.getBindings();
643+
for (unsigned i = 0; i < Bindings.size(); ++i) {
644+
// Skip protocol requirements (FIXME: for now?)
645+
if (Bindings[i].Protocol != nullptr)
646+
continue;
647+
648+
if (Bindings[i].TypeParameter->hasOpenedExistential())
649+
return true;
650+
}
651+
652+
auto ElementTypes = Layout.getElementTypes().slice(
653+
Layout.hasBindings() ? 1 : 0);
654+
for (auto ElementType : ElementTypes) {
655+
auto SwiftType = ElementType.getSwiftRValueType();
656+
if (SwiftType->hasOpenedExistential())
657+
return true;
658+
}
659+
660+
return false;
661+
}
662+
633663
/// Slice off the NecessaryBindings struct at the beginning, if it's there.
634664
/// We'll keep track of how many things are in the bindings struct with its
635665
/// own count in the capture descriptor.
@@ -660,8 +690,9 @@ class CaptureDescriptorBuilder : public ReflectionMetadataBuilder {
660690
continue;
661691

662692
auto Source = SourceBuilder.createClosureBinding(i);
663-
auto BindingType = Caller.mapTypeOutOfContext(Bindings[i].TypeParameter);
664-
SourceMap.push_back({BindingType->getCanonicalType(), Source});
693+
auto BindingType = Bindings[i].TypeParameter;
694+
auto InterfaceType = Caller.mapTypeOutOfContext(BindingType);
695+
SourceMap.push_back({InterfaceType->getCanonicalType(), Source});
665696
}
666697

667698
// Check if any requirements were fulfilled by metadata stored inside a
@@ -701,10 +732,9 @@ class CaptureDescriptorBuilder : public ReflectionMetadataBuilder {
701732
// parameters.
702733
auto Src = Path.getMetadataSource(SourceBuilder, Root);
703734

704-
auto SubstType =
705-
Caller.mapTypeOutOfContext(
706-
GenericParam.subst(SubstMap, None));
707-
SourceMap.push_back({SubstType->getCanonicalType(), Src});
735+
auto SubstType = GenericParam.subst(SubstMap);
736+
auto InterfaceType = Caller.mapTypeOutOfContext(SubstType);
737+
SourceMap.push_back({InterfaceType->getCanonicalType(), Src});
708738
});
709739

710740
return SourceMap;
@@ -872,11 +902,13 @@ IRGenModule::getAddrOfCaptureDescriptor(SILFunction &Caller,
872902
if (!IRGen.Opts.EnableReflectionMetadata)
873903
return llvm::Constant::getNullValue(CaptureDescriptorPtrTy);
874904

905+
if (CaptureDescriptorBuilder::hasOpenedExistential(OrigCalleeType, Layout))
906+
return llvm::Constant::getNullValue(CaptureDescriptorPtrTy);
907+
875908
CaptureDescriptorBuilder builder(*this, Caller,
876909
OrigCalleeType, SubstCalleeType, Subs,
877910
Layout);
878911
auto var = builder.emit();
879-
880912
return llvm::ConstantExpr::getBitCast(var, CaptureDescriptorPtrTy);
881913
}
882914

lib/IRGen/GenType.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1531,8 +1531,9 @@ llvm::StructType *IRGenModule::createNominalType(CanType type) {
15311531
assert(type.getNominalOrBoundGenericNominal());
15321532

15331533
// We share type infos for different instantiations of a generic type
1534-
// when the archetypes have the same exemplars. Mangling the archetypes
1535-
// in this case can be very misleading, so we just mangle the base name.
1534+
// when the archetypes have the same exemplars. We cannot mangle
1535+
// archetypes, and the mangling does not have to be unique, so we just
1536+
// mangle the unbound generic form of the type.
15361537
if (type->hasArchetype())
15371538
type = type.getNominalOrBoundGenericNominal()->getDeclaredType()
15381539
->getCanonicalType();

lib/IRGen/IRGenMangler.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,18 @@ mangleProtocolForLLVMTypeName(ProtocolCompositionType *type) {
9494
appendOperator("_");
9595
}
9696
if (layout.superclass) {
97-
appendType(layout.superclass);
97+
auto superclassTy = layout.superclass;
98+
99+
// We share type infos for different instantiations of a generic type
100+
// when the archetypes have the same exemplars. We cannot mangle
101+
// archetypes, and the mangling does not have to be unique, so we just
102+
// mangle the unbound generic form of the type.
103+
if (superclassTy->hasArchetype()) {
104+
superclassTy = superclassTy->getClassOrBoundGenericClass()
105+
->getDeclaredType();
106+
}
107+
108+
appendType(CanType(superclassTy));
98109
appendOperator("Xc");
99110
} else if (layout.getLayoutConstraint()) {
100111
appendOperator("Xl");

0 commit comments

Comments
 (0)