Skip to content

Commit 885d221

Browse files
authored
Merge pull request swiftlang#76585 from slavapestov/small-subst-cleanups-2
More type substitution cleanups
2 parents 961e6ea + 7042ee4 commit 885d221

16 files changed

+198
-171
lines changed

include/swift/AST/PackConformance.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ class alignas(1 << DeclAlignInBits) PackConformance final
7474

7575
PackConformance *getCanonicalConformance() const;
7676

77-
PackType *getAssociatedType(Type assocType) const;
77+
PackType *getTypeWitness(AssociatedTypeDecl *assocType,
78+
SubstOptions options=std::nullopt) const;
7879

7980
PackConformance *
8081
getAssociatedConformance(Type assocType, ProtocolDecl *protocol) const;

include/swift/AST/ProtocolConformanceRef.h

+5
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@ class ProtocolConformanceRef {
170170
/// Map contextual types to interface types in the conformance.
171171
ProtocolConformanceRef mapConformanceOutOfContext() const;
172172

173+
/// Look up the type witness for an associated type declaration in this
174+
/// conformance.
175+
Type getTypeWitness(Type origType, AssociatedTypeDecl *assocType,
176+
SubstOptions options = std::nullopt) const;
177+
173178
/// Given a dependent type (expressed in terms of this conformance's
174179
/// protocol), follow it from the conforming type.
175180
Type getAssociatedType(Type origType, Type dependentType) const;

lib/AST/GenericSignature.cpp

+35-2
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,8 @@ bool GenericSignatureImpl::isRequirementSatisfied(
412412

413413
requirement = requirement.subst(
414414
QueryInterfaceTypeSubstitutions{genericEnv},
415-
LookUpConformanceInModule());
415+
LookUpConformanceInModule(),
416+
SubstFlags::PreservePackExpansionLevel);
416417
}
417418

418419
SmallVector<Requirement, 2> subReqs;
@@ -1173,7 +1174,39 @@ void swift::validateGenericSignature(ASTContext &context,
11731174

11741175
// If the removed requirement is satisfied by the new generic signature,
11751176
// it is redundant. Complain.
1176-
if (newSig->isRequirementSatisfied(requirements[victimIndex])) {
1177+
auto satisfied = [&](Requirement victim) {
1178+
if (!newSig->isValidTypeParameter(victim.getFirstType()))
1179+
return false;
1180+
1181+
switch (victim.getKind()) {
1182+
case RequirementKind::SameShape:
1183+
return (newSig->isValidTypeParameter(victim.getSecondType()) &&
1184+
newSig->haveSameShape(victim.getFirstType(),
1185+
victim.getSecondType()));
1186+
case RequirementKind::Conformance:
1187+
return newSig->requiresProtocol(victim.getFirstType(),
1188+
victim.getProtocolDecl());
1189+
case RequirementKind::Superclass: {
1190+
auto superclass = newSig->getSuperclassBound(victim.getFirstType());
1191+
return (superclass && superclass->isEqual(victim.getSecondType()));
1192+
}
1193+
case RequirementKind::SameType:
1194+
if (!victim.getSecondType().findIf([&](Type t) -> bool {
1195+
return (!t->isTypeParameter() ||
1196+
newSig->isValidTypeParameter(t));
1197+
})) {
1198+
return false;
1199+
}
1200+
return newSig.getReducedType(victim.getFirstType())
1201+
->isEqual(newSig.getReducedType(victim.getSecondType()));
1202+
case RequirementKind::Layout: {
1203+
auto layout = newSig->getLayoutConstraint(victim.getFirstType());
1204+
return (layout && layout == victim.getLayoutConstraint());
1205+
}
1206+
}
1207+
};
1208+
1209+
if (satisfied(requirements[victimIndex])) {
11771210
SmallString<32> reqString;
11781211
{
11791212
llvm::raw_svector_ostream out(reqString);

lib/AST/PackConformance.cpp

+6-4
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,9 @@ PackConformance *PackConformance::getCanonicalConformance() const {
9797
/// Project the corresponding associated type from each pack element
9898
/// of the conforming type, collecting the results into a new pack type
9999
/// that has the same pack expansion structure as the conforming type.
100-
PackType *PackConformance::getAssociatedType(Type assocType) const {
100+
PackType *PackConformance::getTypeWitness(
101+
AssociatedTypeDecl *assocType,
102+
SubstOptions options) const {
101103
SmallVector<Type, 4> packElements;
102104

103105
auto conformances = getPatternConformances();
@@ -110,8 +112,8 @@ PackType *PackConformance::getAssociatedType(Type assocType) const {
110112
// conformance.
111113
if (auto *packExpansion = packElement->getAs<PackExpansionType>()) {
112114
auto assocTypePattern =
113-
conformances[i].getAssociatedType(packExpansion->getPatternType(),
114-
assocType);
115+
conformances[i].getTypeWitness(packExpansion->getPatternType(),
116+
assocType, options);
115117

116118
packElements.push_back(PackExpansionType::get(
117119
assocTypePattern, packExpansion->getCountType()));
@@ -120,7 +122,7 @@ PackType *PackConformance::getAssociatedType(Type assocType) const {
120122
// the associated type witness from the pattern conformance.
121123
} else {
122124
auto assocTypeScalar =
123-
conformances[i].getAssociatedType(packElement, assocType);
125+
conformances[i].getTypeWitness(packElement, assocType, options);
124126
packElements.push_back(assocTypeScalar);
125127
}
126128
}

lib/AST/ProtocolConformance.cpp

+21-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,27 @@ ProtocolConformance::getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,
147147

148148
Type ProtocolConformance::getTypeWitness(AssociatedTypeDecl *assocType,
149149
SubstOptions options) const {
150-
return getTypeWitnessAndDecl(assocType, options).getWitnessType();
150+
auto witness = getTypeWitnessAndDecl(assocType, options);
151+
auto witnessTy = witness.getWitnessType();
152+
if (!witnessTy)
153+
return witnessTy;
154+
155+
// This is a hacky feature allowing code completion to migrate to
156+
// using Type::subst() without changing output.
157+
//
158+
// FIXME: Remove this hack and do whatever we need to do in the
159+
// ASTPrinter instead.
160+
if (options & SubstFlags::DesugarMemberTypes) {
161+
if (auto *aliasType = dyn_cast<TypeAliasType>(witnessTy.getPointer()))
162+
witnessTy = aliasType->getSinglyDesugaredType();
163+
164+
// Another hack. If the type witness is a opaque result type. They can
165+
// only be referred using the name of the associated type.
166+
if (witnessTy->is<OpaqueTypeArchetypeType>())
167+
witnessTy = witness.getWitnessDecl()->getDeclaredInterfaceType();
168+
}
169+
170+
return witnessTy;
151171
}
152172

153173
ConcreteDeclRef

lib/AST/ProtocolConformanceRef.cpp

+77-40
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "swift/AST/Availability.h"
2121
#include "swift/AST/ConformanceLookup.h"
2222
#include "swift/AST/Decl.h"
23+
#include "swift/AST/GenericEnvironment.h"
2324
#include "swift/AST/InFlightSubstitution.h"
2425
#include "swift/AST/Module.h"
2526
#include "swift/AST/PackConformance.h"
@@ -163,8 +164,7 @@ ProtocolConformanceRef::getTypeWitnessByName(Type type, Identifier name) const {
163164
if (!assocType)
164165
return ErrorType::get(proto->getASTContext());
165166

166-
return assocType->getDeclaredInterfaceType().subst(
167-
SubstitutionMap::getProtocolSubstitutions(proto, type, *this));
167+
return getTypeWitness(type, assocType);
168168
}
169169

170170
ConcreteDeclRef
@@ -194,78 +194,115 @@ ProtocolConformanceRef::getConditionalRequirements() const {
194194
return {};
195195
}
196196

197-
Type ProtocolConformanceRef::getAssociatedType(Type conformingType,
198-
Type assocType) const {
197+
Type ProtocolConformanceRef::getTypeWitness(Type conformingType,
198+
AssociatedTypeDecl *assocType,
199+
SubstOptions options) const {
199200
if (isPack()) {
200201
auto *pack = getPack();
201202
ASSERT(conformingType->isEqual(pack->getType()));
202-
return pack->getAssociatedType(assocType);
203+
return pack->getTypeWitness(assocType);
203204
}
204205

205-
auto type = assocType->getCanonicalType();
206-
207-
// Fast path for generic parameters.
208-
if (auto paramTy = dyn_cast<GenericTypeParamType>(type)) {
209-
ASSERT(paramTy->getDepth() == 0 && paramTy->getIndex() == 0 &&
210-
"type parameter in protocol was not Self");
211-
return conformingType;
212-
}
206+
auto failed = [&]() {
207+
return DependentMemberType::get(ErrorType::get(conformingType),
208+
assocType);
209+
};
213210

214211
if (isInvalid())
215-
return ErrorType::get(assocType->getASTContext());
212+
return failed();
216213

217214
auto proto = getRequirement();
215+
ASSERT(assocType->getProtocol() == proto);
218216

219217
if (isConcrete()) {
220-
if (auto selfType = conformingType->getAs<DynamicSelfType>())
221-
conformingType = selfType->getSelfType();
222-
ASSERT(getConcrete()->getType()->isEqual(conformingType));
223-
224-
// Fast path for dependent member types on 'Self' of our associated types.
225-
auto memberType = cast<DependentMemberType>(type);
226-
if (memberType.getBase()->isEqual(proto->getSelfInterfaceType()) &&
227-
memberType->getAssocType()->getProtocol() == proto) {
228-
auto witnessType = getConcrete()->getTypeWitness(
229-
memberType->getAssocType());
230-
if (!witnessType)
231-
return ErrorType::get(assocType->getASTContext());
232-
return witnessType;
233-
}
218+
auto witnessType = getConcrete()->getTypeWitness(assocType, options);
219+
if (!witnessType || witnessType->is<ErrorType>())
220+
return failed();
221+
return witnessType;
234222
}
235223

236-
// General case: consult the substitution map.
224+
ASSERT(isAbstract());
225+
226+
if (auto *archetypeType = conformingType->getAs<ArchetypeType>()) {
227+
return archetypeType->getNestedType(assocType);
228+
}
229+
230+
CONDITIONAL_ASSERT(conformingType->isTypeParameter() ||
231+
conformingType->isTypeVariableOrMember() ||
232+
conformingType->is<UnresolvedType>() ||
233+
conformingType->is<PlaceholderType>());
234+
235+
return DependentMemberType::get(conformingType, assocType);
236+
}
237+
238+
Type ProtocolConformanceRef::getAssociatedType(Type conformingType,
239+
Type assocType) const {
240+
if (isInvalid())
241+
return ErrorType::get(assocType->getASTContext());
242+
243+
auto proto = getRequirement();
244+
237245
auto substMap =
238246
SubstitutionMap::getProtocolSubstitutions(proto, conformingType, *this);
239-
return type.subst(substMap);
247+
return assocType.subst(substMap);
240248
}
241249

242250
ProtocolConformanceRef
243251
ProtocolConformanceRef::getAssociatedConformance(Type conformingType,
244252
Type assocType,
245253
ProtocolDecl *protocol) const {
246-
// If this is a pack conformance, project the associated conformances.
254+
// If this is a pack conformance, project the associated conformances from
255+
// each pack element.
247256
if (isPack()) {
248257
auto *pack = getPack();
249258
assert(conformingType->isEqual(pack->getType()));
250259
return ProtocolConformanceRef(
251260
pack->getAssociatedConformance(assocType, protocol));
252261
}
253262

254-
// If this is a concrete conformance, look up the associated conformance.
263+
// If this is a concrete conformance, project the associated conformance.
255264
if (isConcrete()) {
256265
auto conformance = getConcrete();
257266
assert(conformance->getType()->isEqual(conformingType));
258267
return conformance->getAssociatedConformance(assocType, protocol);
259268
}
260269

261-
// Otherwise, apply the substitution {self -> conformingType}
262-
// to the abstract conformance requirement laid upon the dependent type
263-
// by the protocol.
264-
auto subMap =
265-
SubstitutionMap::getProtocolSubstitutions(getRequirement(),
266-
conformingType, *this);
267-
auto abstractConf = ProtocolConformanceRef(protocol);
268-
return abstractConf.subst(assocType, subMap);
270+
// An associated conformance of an archetype might be known to be
271+
// a concrete conformance, if the subject type is fixed to a concrete
272+
// type in the archetype's generic signature. We don't actually have
273+
// any way to recover the conformance in this case, except via global
274+
// conformance lookup.
275+
//
276+
// However, if we move to a first-class representation of abstract
277+
// conformances where they store their subject types, we can also
278+
// cache the lookups inside the abstract conformance instance too.
279+
if (auto archetypeType = conformingType->getAs<ArchetypeType>()) {
280+
conformingType = archetypeType->getInterfaceType();
281+
auto *genericEnv = archetypeType->getGenericEnvironment();
282+
283+
auto subjectType = assocType.transformRec(
284+
[&](TypeBase *t) -> std::optional<Type> {
285+
if (isa<GenericTypeParamType>(t))
286+
return conformingType;
287+
return std::nullopt;
288+
});
289+
290+
return lookupConformance(
291+
genericEnv->mapTypeIntoContext(subjectType),
292+
protocol);
293+
}
294+
295+
// Associated conformances of type parameters and type variables
296+
// are always abstract, because we don't know the output generic
297+
// signature of the substitution (or in the case of type variables,
298+
// we have no visibility into constraints). See the parallel hack
299+
// to handle this in SubstitutionMap::lookupConformance().
300+
CONDITIONAL_ASSERT(conformingType->isTypeParameter() ||
301+
conformingType->isTypeVariableOrMember() ||
302+
conformingType->is<UnresolvedType>() ||
303+
conformingType->is<PlaceholderType>());
304+
305+
return ProtocolConformanceRef(protocol);
269306
}
270307

271308
/// Check of all types used by the conformance are canonical.

lib/AST/RequirementMachine/ConcreteContraction.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -302,8 +302,7 @@ ConcreteContraction::substTypeParameterRec(Type type, Position position) const {
302302
return std::nullopt;
303303
}
304304

305-
return conformance.getAssociatedType(
306-
*substBaseType, assocType->getDeclaredInterfaceType());
305+
return conformance.getTypeWitness(*substBaseType, assocType);
307306
}
308307

309308
// An unresolved DependentMemberType stores an identifier. Handle this

lib/AST/RequirementMachine/GenericSignatureQueries.cpp

+1-3
Original file line numberDiff line numberDiff line change
@@ -346,9 +346,7 @@ static Type substPrefixType(Type type, unsigned suffixLength, Type prefixType,
346346
auto *assocDecl = memberType->getAssocType();
347347
auto *proto = assocDecl->getProtocol();
348348
auto conformance = lookupConformance(substBaseType, proto);
349-
return conformance.getAssociatedType(
350-
substBaseType,
351-
assocDecl->getDeclaredInterfaceType());
349+
return conformance.getTypeWitness(substBaseType, assocDecl);
352350
}
353351

354352
Type RequirementMachine::getReducedTypeParameter(

lib/AST/RequirementMachine/RequirementLowering.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -665,8 +665,7 @@ struct InferRequirementsWalker : public TypeWalker {
665665
auto addSameTypeConstraint = [&](Type firstType,
666666
AssociatedTypeDecl *assocType) {
667667
auto conformance = lookupConformance(firstType, differentiableProtocol);
668-
auto secondType = conformance.getAssociatedType(
669-
firstType, assocType->getDeclaredInterfaceType());
668+
auto secondType = conformance.getTypeWitness(firstType, assocType);
670669
reqs.push_back({Requirement(RequirementKind::SameType,
671670
firstType, secondType),
672671
SourceLoc()});

lib/AST/Type.cpp

+7-4
Original file line numberDiff line numberDiff line change
@@ -3747,12 +3747,16 @@ void ParameterizedProtocolType::getRequirements(
37473747
assert(argTypes.size() <= assocTypes.size());
37483748

37493749
auto conformance = lookupConformance(baseType, protoDecl);
3750+
auto subMap = SubstitutionMap::getProtocolSubstitutions(
3751+
protoDecl, baseType, conformance);
37503752

37513753
for (unsigned i : indices(argTypes)) {
37523754
auto argType = argTypes[i];
37533755
auto *assocType = assocTypes[i];
3754-
auto subjectType = conformance.getAssociatedType(
3755-
baseType, assocType->getDeclaredInterfaceType());
3756+
// Do a general type substitution here because the associated type might be
3757+
// from an inherited protocol, in which case we will evaluate a non-trivial
3758+
// conformance path.
3759+
auto subjectType = assocType->getDeclaredInterfaceType().subst(subMap);
37563760
reqs.emplace_back(RequirementKind::SameType, subjectType, argType);
37573761
}
37583762
}
@@ -4463,8 +4467,7 @@ TypeBase::getAutoDiffTangentSpace(LookupConformanceFn lookupConformance) {
44634467
// Try to get the `TangentVector` associated type of `base`.
44644468
// Return the associated type if it is valid.
44654469
auto conformance = swift::lookupConformance(this, differentiableProtocol);
4466-
auto assocTy = conformance.getAssociatedType(
4467-
this, assocDecl->getDeclaredInterfaceType());
4470+
auto assocTy = conformance.getTypeWitness(this, assocDecl);
44684471
if (!assocTy->hasError())
44694472
return cache(TangentSpace::getTangentVector(assocTy));
44704473

0 commit comments

Comments
 (0)