Skip to content

Commit 9463b60

Browse files
authored
Merge pull request #8792 from swiftix/wip-canonical-conformances
Add a functionality to produce canonical conformances and substitutions to fix a bug with SILBoxType’s generic arguments
2 parents 2347120 + 07d7790 commit 9463b60

File tree

8 files changed

+197
-11
lines changed

8 files changed

+197
-11
lines changed

include/swift/AST/ProtocolConformance.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,14 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance {
138138
getState() == ProtocolConformanceState::Checking;
139139
}
140140

141+
/// Determine whether this conformance is canonical.
142+
bool isCanonical() const;
143+
144+
/// Create a canonical conformance from the current one.
145+
/// If the current conformance is canonical already, it will be returned.
146+
/// Otherwise a new conformance will be created.
147+
ProtocolConformance *getCanonicalConformance();
148+
141149
/// Return true if the conformance has a witness for the given associated
142150
/// type.
143151
bool hasTypeWitness(AssociatedTypeDecl *assocType,
@@ -703,7 +711,7 @@ class InheritedProtocolConformance : public ProtocolConformance,
703711

704712
static void Profile(llvm::FoldingSetNodeID &ID, Type type,
705713
ProtocolConformance *inheritedConformance) {
706-
ID.AddPointer(type->getCanonicalType().getPointer());
714+
ID.AddPointer(type.getPointer());
707715
ID.AddPointer(inheritedConformance);
708716
}
709717

include/swift/AST/ProtocolConformanceRef.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ class ProtocolConformanceRef {
127127
ProtocolConformanceRef conformance,
128128
Identifier name,
129129
LazyResolver *resolver);
130+
131+
/// Determine whether this conformance is canonical.
132+
bool isCanonical() const;
133+
134+
/// Create a canonical conformance from the current one.
135+
ProtocolConformanceRef getCanonicalConformanceRef() const;
130136
};
131137

132138
} // end namespace swift

include/swift/AST/Substitution.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ class Substitution {
4848

4949
Substitution(Type Replacement, ArrayRef<ProtocolConformanceRef> Conformance);
5050

51+
/// Checks whether the current substitution is canonical.
52+
bool isCanonical() const;
53+
54+
/// Get the canonicalized substitution. If wasCanonical is not nullptr,
55+
/// store there whether the current substitution was canonical already.
56+
Substitution getCanonicalSubstitution(bool *wasCanonical = nullptr) const;
57+
5158
bool operator!=(const Substitution &other) const { return !(*this == other); }
5259
bool operator==(const Substitution &other) const;
5360
void print(llvm::raw_ostream &os,

include/swift/AST/SubstitutionList.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,18 @@ using SubstitutionList = ArrayRef<Substitution>;
2929

3030
void dump(SubstitutionList subs);
3131

32+
/// Create a canonicalized substitution list from subs.
33+
/// subs is the substitution list to be canonicalized.
34+
/// canSubs is an out-parameter, which is used to store the results in case
35+
/// the list of substitutions was not canonical.
36+
/// The function returns a list of canonicalized substitutions.
37+
/// If the substitution list subs was canonical already, it will be returned and
38+
/// canSubs out-parameter will be empty.
39+
/// If something had to be canonicalized, then the canSubs out-parameter will be
40+
/// populated and the returned SubstitutionList would refer to canSubs storage.
41+
SubstitutionList
42+
getCanonicalSubstitutionList(SubstitutionList subs,
43+
SmallVectorImpl<Substitution> &canSubs);
3244
} // end namespace swift
3345

3446
#endif

lib/AST/ASTContext.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4187,8 +4187,13 @@ CanSILBoxType SILBoxType::get(ASTContext &C,
41874187
SILLayout *Layout,
41884188
SubstitutionList Args) {
41894189
llvm::FoldingSetNodeID id;
4190+
4191+
// Canonicalize substitutions.
4192+
SmallVector<Substitution, 4> CanArgs;
4193+
Args = getCanonicalSubstitutionList(Args, CanArgs);
4194+
41904195
Profile(id, Layout, Args);
4191-
4196+
41924197
// Return an existing layout if there is one.
41934198
void *insertPos;
41944199
auto &SILBoxTypes = C.Impl.SILBoxTypes;

lib/AST/ProtocolConformance.cpp

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,3 +1059,149 @@ DeclContext::getLocalConformances(
10591059

10601060
return result;
10611061
}
1062+
1063+
/// Check of all types used by the conformance are canonical.
1064+
bool ProtocolConformance::isCanonical() const {
1065+
// Normal conformances are always canonical by construction.
1066+
if (getKind() == ProtocolConformanceKind::Normal)
1067+
return true;
1068+
1069+
if (!getType()->isCanonical())
1070+
return false;
1071+
if (!getInterfaceType()->isCanonical())
1072+
return false;
1073+
1074+
switch (getKind()) {
1075+
case ProtocolConformanceKind::Normal: {
1076+
return true;
1077+
}
1078+
case ProtocolConformanceKind::Inherited: {
1079+
// Substitute the base.
1080+
auto inheritedConformance
1081+
= cast<InheritedProtocolConformance>(this);
1082+
return inheritedConformance->getInheritedConformance()->isCanonical();
1083+
}
1084+
case ProtocolConformanceKind::Specialized: {
1085+
// Substitute the substitutions in the specialized conformance.
1086+
auto spec = cast<SpecializedProtocolConformance>(this);
1087+
auto genericConformance = spec->getGenericConformance();
1088+
if (!genericConformance->isCanonical())
1089+
return false;
1090+
auto specSubs = spec->getGenericSubstitutions();
1091+
for (const auto &sub : specSubs) {
1092+
if (!sub.isCanonical())
1093+
return false;
1094+
}
1095+
return true;
1096+
}
1097+
}
1098+
llvm_unreachable("bad ProtocolConformanceKind");
1099+
}
1100+
1101+
Substitution Substitution::getCanonicalSubstitution(bool *wasCanonical) const {
1102+
bool createdNewCanonicalConformances = false;
1103+
bool createdCanReplacement = false;
1104+
SmallVector<ProtocolConformanceRef, 4> newCanConformances;
1105+
1106+
CanType canReplacement = getReplacement()->getCanonicalType();
1107+
1108+
if (!getReplacement()->isCanonical()) {
1109+
createdCanReplacement = true;
1110+
}
1111+
1112+
for (auto conf : getConformances()) {
1113+
if (conf.isCanonical()) {
1114+
newCanConformances.push_back(conf);
1115+
continue;
1116+
}
1117+
newCanConformances.push_back(conf.getCanonicalConformanceRef());
1118+
createdNewCanonicalConformances = true;
1119+
}
1120+
1121+
ArrayRef<ProtocolConformanceRef> canConformances = getConformances();
1122+
if (createdNewCanonicalConformances) {
1123+
auto &C = canReplacement->getASTContext();
1124+
canConformances = C.AllocateCopy(newCanConformances);
1125+
}
1126+
1127+
if (createdCanReplacement || createdNewCanonicalConformances) {
1128+
if (wasCanonical)
1129+
*wasCanonical = false;
1130+
return Substitution(canReplacement, canConformances);
1131+
}
1132+
if (wasCanonical)
1133+
*wasCanonical = true;
1134+
return *this;
1135+
}
1136+
1137+
SubstitutionList
1138+
swift::getCanonicalSubstitutionList(SubstitutionList subs,
1139+
SmallVectorImpl<Substitution> &canSubs) {
1140+
bool subListWasCanonical = true;
1141+
for (auto &sub : subs) {
1142+
bool subWasCanonical = false;
1143+
auto canSub = sub.getCanonicalSubstitution(&subWasCanonical);
1144+
if (!subWasCanonical)
1145+
subListWasCanonical = false;
1146+
canSubs.push_back(canSub);
1147+
}
1148+
1149+
if (subListWasCanonical) {
1150+
canSubs.clear();
1151+
return subs;
1152+
}
1153+
1154+
subs = canSubs;
1155+
return subs;
1156+
}
1157+
1158+
/// Check of all types used by the conformance are canonical.
1159+
ProtocolConformance *ProtocolConformance::getCanonicalConformance() {
1160+
if (isCanonical())
1161+
return this;
1162+
1163+
switch (getKind()) {
1164+
case ProtocolConformanceKind::Normal: {
1165+
// Normal conformances are always canonical by construction.
1166+
return this;
1167+
}
1168+
1169+
case ProtocolConformanceKind::Inherited: {
1170+
auto &Ctx = getType()->getASTContext();
1171+
auto inheritedConformance = cast<InheritedProtocolConformance>(this);
1172+
return Ctx.getInheritedConformance(
1173+
getType()->getCanonicalType(),
1174+
inheritedConformance->getInheritedConformance()
1175+
->getCanonicalConformance());
1176+
}
1177+
1178+
case ProtocolConformanceKind::Specialized: {
1179+
auto &Ctx = getType()->getASTContext();
1180+
// Substitute the substitutions in the specialized conformance.
1181+
auto spec = cast<SpecializedProtocolConformance>(this);
1182+
auto genericConformance = spec->getGenericConformance();
1183+
auto specSubs = spec->getGenericSubstitutions();
1184+
SmallVector<Substitution, 4> newSpecSubs;
1185+
auto canSpecSubs = getCanonicalSubstitutionList(specSubs, newSpecSubs);
1186+
return Ctx.getSpecializedConformance(
1187+
getType()->getCanonicalType(),
1188+
genericConformance->getCanonicalConformance(),
1189+
newSpecSubs.empty() ? canSpecSubs : Ctx.AllocateCopy(canSpecSubs));
1190+
}
1191+
}
1192+
llvm_unreachable("bad ProtocolConformanceKind");
1193+
}
1194+
1195+
/// Check of all types used by the conformance are canonical.
1196+
bool ProtocolConformanceRef::isCanonical() const {
1197+
if (isAbstract())
1198+
return true;
1199+
return getConcrete()->isCanonical();
1200+
}
1201+
1202+
ProtocolConformanceRef
1203+
ProtocolConformanceRef::getCanonicalConformanceRef() const {
1204+
if (isAbstract())
1205+
return *this;
1206+
return ProtocolConformanceRef(getConcrete()->getCanonicalConformance());
1207+
}

lib/AST/Substitution.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,13 @@ Substitution::Substitution(Type Replacement,
4242
assert(Replacement->isMaterializable()
4343
&& "cannot substitute with a non-materializable type");
4444
}
45+
46+
bool Substitution::isCanonical() const {
47+
if (!getReplacement()->isCanonical())
48+
return false;
49+
for (auto conf : getConformances()) {
50+
if (!conf.isCanonical())
51+
return false;
52+
}
53+
return true;
54+
}

lib/SIL/TypeLowering.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2492,13 +2492,6 @@ TypeConverter::checkFunctionForABIDifferences(SILFunctionType *fnTy1,
24922492
return ABIDifference::Trivial;
24932493
}
24942494

2495-
void canonicalizeSubstitutions(MutableArrayRef<Substitution> subs) {
2496-
for (auto &sub : subs) {
2497-
sub = Substitution(sub.getReplacement()->getCanonicalType(),
2498-
sub.getConformances());
2499-
}
2500-
}
2501-
25022495
CanSILBoxType
25032496
TypeConverter::getInterfaceBoxTypeForCapture(ValueDecl *captured,
25042497
CanType loweredInterfaceType,
@@ -2536,8 +2529,7 @@ TypeConverter::getInterfaceBoxTypeForCapture(ValueDecl *captured,
25362529
return ProtocolConformanceRef(conformedTy->getDecl());
25372530
},
25382531
genericArgs);
2539-
canonicalizeSubstitutions(genericArgs);
2540-
2532+
25412533
auto boxTy = SILBoxType::get(C, layout, genericArgs);
25422534
#ifndef NDEBUG
25432535
// FIXME: Map the box type out of context when asserting the field so

0 commit comments

Comments
 (0)