Skip to content

Commit 0f30686

Browse files
committed
IRGen: Rewrite isSpecializedNominalTypeMetadataStaticallyAddressable()
The old logic looked at each generic parameter, and each conformance of that generic parameter. This missed conformance requirements where the subject type is a dependent member type. Also, the check for dependent witness tables was too strict, because it should have skipped protocols without witness tables. Refactor everything to instead walk the substitution map directly, and check each replacement type and conformance. This simplifies the logic and fixes failures with non-copyable generics enabled.
1 parent d9c82b1 commit 0f30686

File tree

2 files changed

+75
-88
lines changed

2 files changed

+75
-88
lines changed

lib/IRGen/MetadataRequest.cpp

Lines changed: 73 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ static MetadataResponse emitNominalPrespecializedGenericMetadataRef(
656656
DynamicMetadataRequest request,
657657
SpecializedMetadataCanonicality canonicality) {
658658
assert(isCompleteSpecializedNominalTypeMetadataStaticallyAddressable(
659-
IGF.IGM, *theDecl, theType, canonicality));
659+
IGF.IGM, theType, canonicality));
660660
// We are applying generic parameters to a generic type.
661661
assert(theType->getAnyNominal() == theDecl);
662662

@@ -763,16 +763,16 @@ static MetadataResponse emitNominalMetadataRef(IRGenFunction &IGF,
763763
MetadataResponse response;
764764

765765
if (isCompleteSpecializedNominalTypeMetadataStaticallyAddressable(
766-
IGF.IGM, *theDecl, theType, CanonicalSpecializedMetadata)) {
766+
IGF.IGM, theType, CanonicalSpecializedMetadata)) {
767767
response = emitNominalPrespecializedGenericMetadataRef(
768768
IGF, theDecl, theType, request, CanonicalSpecializedMetadata);
769769
} else if (isCompleteSpecializedNominalTypeMetadataStaticallyAddressable(
770-
IGF.IGM, *theDecl, theType, NoncanonicalSpecializedMetadata)) {
770+
IGF.IGM, theType, NoncanonicalSpecializedMetadata)) {
771771
response = emitNominalPrespecializedGenericMetadataRef(
772772
IGF, theDecl, theType, request, NoncanonicalSpecializedMetadata);
773773
} else if (auto theClass = dyn_cast<ClassDecl>(theDecl)) {
774774
if (isSpecializedNominalTypeMetadataStaticallyAddressable(
775-
IGF.IGM, *theClass, theType, CanonicalSpecializedMetadata,
775+
IGF.IGM, theType, CanonicalSpecializedMetadata,
776776
ForUseOnlyFromAccessor)) {
777777
llvm::Function *accessor =
778778
IGF.IGM
@@ -799,10 +799,13 @@ static MetadataResponse emitNominalMetadataRef(IRGenFunction &IGF,
799799
}
800800

801801
bool irgen::isSpecializedNominalTypeMetadataStaticallyAddressable(
802-
IRGenModule &IGM, NominalTypeDecl &nominal, CanType type,
802+
IRGenModule &IGM, CanType type,
803803
SpecializedMetadataCanonicality canonicality,
804804
SpecializedMetadataUsageIsOnlyFromAccessor onlyFromAccessor) {
805-
assert(nominal.isGenericContext());
805+
auto *nominal = type->getAnyNominal();
806+
807+
assert(!isa<ProtocolDecl>(nominal));
808+
assert(nominal->isGenericContext());
806809

807810
if (!IGM.shouldPrespecializeGenericMetadata()) {
808811
return false;
@@ -812,13 +815,16 @@ bool irgen::isSpecializedNominalTypeMetadataStaticallyAddressable(
812815
return false;
813816
}
814817

818+
if (!IGM.getTypeInfoForUnlowered(type).isFixedSize(ResilienceExpansion::Maximal))
819+
return false;
820+
815821
switch (canonicality) {
816822
case CanonicalSpecializedMetadata:
817823
if (IGM.getSILModule().isWholeModule()) {
818824
// Canonical prespecializations can only be emitted within the module
819825
// where the generic type is itself defined, since it is the module where
820826
// the metadata accessor is defined.
821-
if (IGM.getSwiftModule() != nominal.getModuleContext()) {
827+
if (IGM.getSwiftModule() != nominal->getModuleContext()) {
822828
return false;
823829
}
824830
} else {
@@ -827,7 +833,7 @@ bool irgen::isSpecializedNominalTypeMetadataStaticallyAddressable(
827833
// containing the type's decl! The reason is that the generic metadata
828834
// accessor is defined in the IRGenModule corresponding to the source file
829835
// containing the type's decl.
830-
SourceFile *nominalFile = nominal.getDeclContext()->getParentSourceFile();
836+
SourceFile *nominalFile = nominal->getDeclContext()->getParentSourceFile();
831837
if (auto *moduleFile = IGM.IRGen.getSourceFile(&IGM)) {
832838
if (nominalFile != moduleFile) {
833839
return false;
@@ -838,17 +844,17 @@ bool irgen::isSpecializedNominalTypeMetadataStaticallyAddressable(
838844
case NoncanonicalSpecializedMetadata:
839845
// Non-canonical metadata prespecializations for a type cannot be formed
840846
// within the module that defines that type.
841-
if (IGM.getSwiftModule() == nominal.getModuleContext()) {
847+
if (IGM.getSwiftModule() == nominal->getModuleContext()) {
842848
return false;
843849
}
844-
if (nominal.isResilient(IGM.getSwiftModule(),
845-
ResilienceExpansion::Maximal)) {
850+
if (nominal->isResilient(IGM.getSwiftModule(),
851+
ResilienceExpansion::Maximal)) {
846852
return false;
847853
}
848854
break;
849855
}
850856

851-
if (auto *theClass = dyn_cast<ClassDecl>(&nominal)) {
857+
if (auto *theClass = dyn_cast<ClassDecl>(nominal)) {
852858
if (theClass->hasResilientMetadata(IGM.getSwiftModule(),
853859
ResilienceExpansion::Maximal)) {
854860
return false;
@@ -871,77 +877,67 @@ bool irgen::isSpecializedNominalTypeMetadataStaticallyAddressable(
871877
}
872878
}
873879

874-
auto *generic = type.getAnyGeneric();
875-
assert(generic);
876-
auto *environment = generic->getGenericEnvironment();
877-
assert(environment);
880+
// Analyze the substitution map to determine if everything can be referenced
881+
// statically.
878882
auto substitutions =
879-
type->getContextSubstitutionMap(IGM.getSwiftModule(), &nominal);
880-
881-
auto allArgumentsAreStaticallyAddressable =
882-
llvm::all_of(environment->getGenericParams(), [&](auto parameter) {
883-
auto signature = environment->getGenericSignature();
884-
const auto protocols = signature->getRequiredProtocols(parameter);
885-
auto argument = ((Type *)parameter)->subst(substitutions);
886-
auto canonicalType = argument->getCanonicalType();
887-
auto witnessTablesAreReferenceable = [&]() {
888-
return llvm::all_of(protocols, [&](ProtocolDecl *protocol) {
889-
auto conformance =
890-
signature->lookupConformance(canonicalType, protocol);
891-
if (!conformance.isConcrete()) {
892-
return false;
893-
}
894-
auto rootConformance =
895-
conformance.getConcrete()->getRootConformance();
896-
return !IGM.isDependentConformance(rootConformance) &&
897-
!IGM.isResilientConformance(rootConformance);
898-
});
899-
};
900-
// TODO: Once witness tables are statically specialized, check whether
901-
// the
902-
// ConformanceInfo returns nullptr from tryGetConstantTable.
903-
auto isGenericWithoutPrespecializedConformance = [&]() {
904-
auto genericArgument = argument->getAnyGeneric();
905-
return genericArgument && genericArgument->isGenericContext() &&
906-
(protocols.size() > 0);
907-
};
908-
auto metadataAccessIsTrivial = [&]() {
909-
if (onlyFromAccessor) {
910-
// If an accessor is being used, then the accessor will be able to
911-
// initialize the arguments, i.e. register classes with the ObjC
912-
// runtime.
913-
return irgen::
914-
isCanonicalInitializableTypeMetadataStaticallyAddressable(
915-
IGM, canonicalType);
916-
} else {
917-
return irgen::isCanonicalCompleteTypeMetadataStaticallyAddressable(
918-
IGM, canonicalType);
919-
}
920-
};
921-
return !isGenericWithoutPrespecializedConformance() &&
922-
metadataAccessIsTrivial() && witnessTablesAreReferenceable();
923-
});
924-
return allArgumentsAreStaticallyAddressable &&
925-
IGM.getTypeInfoForUnlowered(type).isFixedSize(
926-
ResilienceExpansion::Maximal);
883+
type->getContextSubstitutionMap(IGM.getSwiftModule(), nominal);
884+
885+
// If we cannot statically reference type metadata for our replacement types,
886+
// we cannot specialize.
887+
for (auto replacementType : substitutions.getReplacementTypes()) {
888+
auto canonicalType = replacementType->getCanonicalType();
889+
if (onlyFromAccessor) {
890+
// If an accessor is being used, then the accessor will be able to
891+
// initialize the arguments, i.e. register classes with the ObjC
892+
// runtime.
893+
if (!irgen::isCanonicalInitializableTypeMetadataStaticallyAddressable(
894+
IGM, canonicalType)) {
895+
return false;
896+
}
897+
} else {
898+
if (!irgen::isCanonicalCompleteTypeMetadataStaticallyAddressable(
899+
IGM, canonicalType)) {
900+
return false;
901+
}
902+
}
903+
}
904+
905+
// If we have to instantiate resilient or dependent witness tables, we
906+
// cannot prespecialize.
907+
for (auto conformance : substitutions.getConformances()) {
908+
auto protocol = conformance.getRequirement();
909+
if (!Lowering::TypeConverter::protocolRequiresWitnessTable(protocol))
910+
continue;
911+
912+
if (!conformance.isConcrete())
913+
return false;
914+
915+
auto rootConformance = conformance.getConcrete()->getRootConformance();
916+
if (IGM.isDependentConformance(rootConformance) ||
917+
IGM.isResilientConformance(rootConformance))
918+
return false;
919+
}
920+
921+
return true;
927922
}
928923

929924
bool irgen::isCompleteSpecializedNominalTypeMetadataStaticallyAddressable(
930-
IRGenModule &IGM, NominalTypeDecl &nominal, CanType type,
925+
IRGenModule &IGM, CanType type,
931926
SpecializedMetadataCanonicality canonicality) {
932927
if (isa<ClassType>(type) || isa<BoundGenericClassType>(type)) {
933928
// TODO: On platforms without ObjC interop, we can do direct access to
934929
// class metadata.
935930
return false;
936931
}
932+
937933
// Prespecialized struct/enum metadata gets no dedicated accessor yet and so
938934
// cannot do the work of registering the generic arguments which are classes
939935
// with the ObjC runtime. Concretely, the following cannot be prespecialized
940936
// yet:
941937
// Struct<Klass<Int>>
942938
// Enum<Klass<Int>>
943939
return isSpecializedNominalTypeMetadataStaticallyAddressable(
944-
IGM, nominal, type, canonicality, NotForUseOnlyFromAccessor);
940+
IGM, type, canonicality, NotForUseOnlyFromAccessor);
945941
}
946942

947943
/// Is there a known address for canonical specialized metadata? The metadata
@@ -954,14 +950,17 @@ bool irgen::isCanonicalInitializableTypeMetadataStaticallyAddressable(
954950
return true;
955951
}
956952

957-
NominalTypeDecl *nominal;
958-
if ((nominal = type->getAnyNominal()) && nominal->isGenericContext()) {
953+
if (isa<ExistentialType>(type))
954+
return false;
955+
956+
auto *nominal = type->getAnyNominal();
957+
if (nominal && nominal->isGenericContext()) {
959958
// Prespecialized class metadata gets a dedicated accessor which can do
960959
// the work of registering the class and its arguments with the ObjC
961960
// runtime.
962961
// Concretely, Clazz<Klass<Int>> can be prespecialized.
963962
return isSpecializedNominalTypeMetadataStaticallyAddressable(
964-
IGM, *nominal, type, CanonicalSpecializedMetadata,
963+
IGM, type, CanonicalSpecializedMetadata,
965964
ForUseOnlyFromAccessor);
966965
}
967966

@@ -977,15 +976,12 @@ bool irgen::isNoncanonicalCompleteTypeMetadataStaticallyAddressable(
977976
}
978977

979978
if (isa<BoundGenericStructType>(type) || isa<BoundGenericEnumType>(type)) {
980-
auto nominalType = cast<BoundGenericType>(type);
981-
auto *nominalDecl = nominalType->getDecl();
982-
983979
// Imported type metadata always requires an accessor.
984-
if (isa<ClangModuleUnit>(nominalDecl->getModuleScopeContext()))
980+
if (isa<ClangModuleUnit>(type->getAnyNominal()->getModuleScopeContext()))
985981
return false;
986982

987983
return isCompleteSpecializedNominalTypeMetadataStaticallyAddressable(
988-
IGM, *nominalDecl, type, NoncanonicalSpecializedMetadata);
984+
IGM, type, NoncanonicalSpecializedMetadata);
989985
}
990986
return false;
991987
}
@@ -1007,11 +1003,9 @@ bool irgen::isCanonicalCompleteTypeMetadataStaticallyAddressable(
10071003

10081004
if (nominalDecl->isGenericContext())
10091005
return isCompleteSpecializedNominalTypeMetadataStaticallyAddressable(
1010-
IGM, *nominalDecl, type, CanonicalSpecializedMetadata);
1006+
IGM, type, CanonicalSpecializedMetadata);
10111007

10121008
auto expansion = ResilienceExpansion::Maximal;
1013-
1014-
// Resiliently-sized metadata access always requires an accessor.
10151009
return IGM.getTypeInfoForUnlowered(type).isFixedSize(expansion);
10161010
}
10171011

@@ -1034,15 +1028,8 @@ bool irgen::isCanonicalCompleteTypeMetadataStaticallyAddressable(
10341028
return true;
10351029

10361030
if (isa<BoundGenericStructType>(type) || isa<BoundGenericEnumType>(type)) {
1037-
auto nominalType = cast<BoundGenericType>(type);
1038-
auto *nominalDecl = nominalType->getDecl();
1039-
1040-
// Imported type metadata always requires an accessor.
1041-
if (isa<ClangModuleUnit>(nominalDecl->getModuleScopeContext()))
1042-
return false;
1043-
10441031
return isCompleteSpecializedNominalTypeMetadataStaticallyAddressable(
1045-
IGM, *nominalDecl, type, CanonicalSpecializedMetadata);
1032+
IGM, type, CanonicalSpecializedMetadata);
10461033
}
10471034

10481035
return false;
@@ -1066,7 +1053,7 @@ bool irgen::shouldCacheTypeMetadataAccess(IRGenModule &IGM, CanType type) {
10661053
return true;
10671054
if (classDecl->isGenericContext() &&
10681055
isSpecializedNominalTypeMetadataStaticallyAddressable(
1069-
IGM, *classDecl, type, CanonicalSpecializedMetadata,
1056+
IGM, type, CanonicalSpecializedMetadata,
10701057
ForUseOnlyFromAccessor))
10711058
return false;
10721059
auto strategy = IGM.getClassMetadataStrategy(classDecl);

lib/IRGen/MetadataRequest.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -536,14 +536,14 @@ enum SpecializedMetadataCanonicality : bool {
536536
/// known, but access to the metadata must go through the canonical specialized
537537
/// accessor so that initialization of the metadata can occur.
538538
bool isSpecializedNominalTypeMetadataStaticallyAddressable(
539-
IRGenModule &IGM, NominalTypeDecl &nominal, CanType type,
539+
IRGenModule &IGM, CanType type,
540540
SpecializedMetadataCanonicality canonicality,
541541
SpecializedMetadataUsageIsOnlyFromAccessor onlyFromAccessor);
542542

543543
/// Is the address of a specialization of the generic metadata which does not
544544
/// require runtime initialization statically known?
545545
bool isCompleteSpecializedNominalTypeMetadataStaticallyAddressable(
546-
IRGenModule &IGM, NominalTypeDecl &nominal, CanType type,
546+
IRGenModule &IGM, CanType type,
547547
SpecializedMetadataCanonicality canonicality);
548548

549549
/// Is the address of canonical metadata which may need to be initialized (e.g.

0 commit comments

Comments
 (0)