Skip to content

Commit a42fe56

Browse files
authored
Merge pull request #18978 from slavapestov/clean-up-conformance-subst
Clean up ProtocolConformance::subst() and fix crash exposed by coroutines work
2 parents f3f07ec + a71710b commit a42fe56

File tree

11 files changed

+148
-128
lines changed

11 files changed

+148
-128
lines changed

include/swift/AST/ProtocolConformance.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -315,13 +315,11 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance {
315315

316316
/// Substitute the conforming type and produce a ProtocolConformance that
317317
/// applies to the substituted type.
318-
ProtocolConformance *subst(Type substType,
319-
SubstitutionMap subMap) const;
318+
ProtocolConformance *subst(SubstitutionMap subMap) const;
320319

321320
/// Substitute the conforming type and produce a ProtocolConformance that
322321
/// applies to the substituted type.
323-
ProtocolConformance *subst(Type substType,
324-
TypeSubstitutionFn subs,
322+
ProtocolConformance *subst(TypeSubstitutionFn subs,
325323
LookupConformanceFn conformances) const;
326324

327325
void dump() const;

include/swift/SIL/TypeSubstCloner.h

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -297,15 +297,12 @@ class TypeSubstCloner : public SILClonerWithScopes<ImplClass> {
297297
if (SubsMap.empty())
298298
return false;
299299

300-
auto Params = Sig->getSubstitutableParams();
301-
return std::any_of(Params.begin(), Params.end(), [&](Type ParamType) {
302-
// FIXME: It would be more elegant to run
303-
// SubsMap.mapReplacementTypesOutOfContext() bup front, but it can assert.
304-
Type Substitution = Type(ParamType).subst(SubsMap)->mapTypeOutOfContext();
305-
return !Substitution->isOpenedExistential() &&
306-
(Substitution->getCanonicalType() !=
307-
ParamType->getCanonicalType());
308-
});
300+
for (auto ParamType : Sig->getSubstitutableParams()) {
301+
if (!Type(ParamType).subst(SubsMap)->isEqual(ParamType))
302+
return true;
303+
}
304+
305+
return false;
309306
}
310307

311308
enum { ForInlining = true };
@@ -314,7 +311,7 @@ class TypeSubstCloner : public SILClonerWithScopes<ImplClass> {
314311
/// \param SubsMap - the substitutions of the inlining/specialization process.
315312
/// \param RemappedSig - the generic signature.
316313
static SILFunction *remapParentFunction(FunctionBuilderTy &FuncBuilder,
317-
SILModule &M,
314+
SILModule &M,
318315
SILFunction *ParentFunction,
319316
SubstitutionMap SubsMap,
320317
GenericSignature *RemappedSig,
@@ -325,14 +322,15 @@ class TypeSubstCloner : public SILClonerWithScopes<ImplClass> {
325322
if (!RemappedSig || !OriginalEnvironment)
326323
return ParentFunction;
327324

328-
if (!substitutionsChangeGenericTypeParameters(SubsMap, RemappedSig))
329-
return ParentFunction;
330-
331325
if (SubsMap.hasArchetypes())
332326
SubsMap = SubsMap.mapReplacementTypesOutOfContext();
333327

334-
// This is a bug in mapReplacementTypesOutOfContext(). Archetypes can't be
335-
// mangled, only type parameters can; ignore this for now.
328+
if (!substitutionsChangeGenericTypeParameters(SubsMap, RemappedSig))
329+
return ParentFunction;
330+
331+
// Note that mapReplacementTypesOutOfContext() can't do anything for
332+
// opened existentials, and since archetypes can't be mangled, ignore
333+
// this case for now.
336334
if (SubsMap.hasArchetypes())
337335
return ParentFunction;
338336

lib/AST/ProtocolConformance.cpp

Lines changed: 35 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -98,22 +98,14 @@ ProtocolConformanceRef::subst(Type origType,
9898
if (isInvalid())
9999
return *this;
100100

101-
auto substType = origType.subst(subs, conformances,
102-
SubstFlags::UseErrorType);
103-
104101
// If we have a concrete conformance, we need to substitute the
105102
// conformance to apply to the new type.
106-
if (isConcrete()) {
107-
auto concrete = getConcrete();
108-
if (auto classDecl = concrete->getType()->getClassOrBoundGenericClass()) {
109-
// If this is a class, we need to traffic in the actual type that
110-
// implements the protocol, not 'Self' and not any subclasses (with their
111-
// inherited conformances).
112-
substType = substType->getSuperclassForDecl(classDecl);
113-
}
114-
return ProtocolConformanceRef(
115-
getConcrete()->subst(substType, subs, conformances));
116-
}
103+
if (isConcrete())
104+
return ProtocolConformanceRef(getConcrete()->subst(subs, conformances));
105+
106+
// Otherwise, compute the substituted type.
107+
auto substType = origType.subst(subs, conformances,
108+
SubstFlags::UseErrorType);
117109

118110
// Opened existentials trivially conform and do not need to go through
119111
// substitution map lookup.
@@ -1047,74 +1039,64 @@ bool ProtocolConformance::isVisibleFrom(const DeclContext *dc) const {
10471039
}
10481040

10491041
ProtocolConformance *
1050-
ProtocolConformance::subst(Type substType,
1051-
SubstitutionMap subMap) const {
1052-
return subst(substType,
1053-
QuerySubstitutionMap{subMap},
1042+
ProtocolConformance::subst(SubstitutionMap subMap) const {
1043+
return subst(QuerySubstitutionMap{subMap},
10541044
LookUpConformanceInSubstitutionMap(subMap));
10551045
}
10561046

10571047
ProtocolConformance *
1058-
ProtocolConformance::subst(Type substType,
1059-
TypeSubstitutionFn subs,
1048+
ProtocolConformance::subst(TypeSubstitutionFn subs,
10601049
LookupConformanceFn conformances) const {
1061-
// ModuleDecl::lookupConformance() strips off dynamic Self, so
1062-
// we should do the same here.
1063-
if (auto selfType = substType->getAs<DynamicSelfType>())
1064-
substType = selfType->getSelfType();
1065-
1066-
if (getType()->isEqual(substType))
1067-
return const_cast<ProtocolConformance *>(this);
1068-
10691050
switch (getKind()) {
10701051
case ProtocolConformanceKind::Normal: {
1071-
if (substType->isSpecialized()) {
1072-
assert(getType()->isSpecialized()
1073-
&& "substitution mapped non-specialized to specialized?!");
1074-
assert(getType()->getNominalOrBoundGenericNominal()
1075-
== substType->getNominalOrBoundGenericNominal()
1076-
&& "substitution mapped to different nominal?!");
1052+
auto origType = getType();
1053+
if (!origType->hasTypeParameter() &&
1054+
!origType->hasArchetype())
1055+
return const_cast<ProtocolConformance *>(this);
10771056

1078-
auto subMap = SubstitutionMap::get(getGenericSignature(),
1079-
subs, conformances);
1057+
auto subMap = SubstitutionMap::get(getGenericSignature(),
1058+
subs, conformances);
1059+
auto substType = origType.subst(subMap, SubstFlags::UseErrorType);
1060+
if (substType->isEqual(origType))
1061+
return const_cast<ProtocolConformance *>(this);
10801062

1081-
return substType->getASTContext()
1063+
return substType->getASTContext()
10821064
.getSpecializedConformance(substType,
10831065
const_cast<ProtocolConformance *>(this),
10841066
subMap);
1085-
}
1086-
1087-
assert(substType->isEqual(getType())
1088-
&& "substitution changed non-specialized type?!");
1089-
return const_cast<ProtocolConformance *>(this);
10901067
}
10911068
case ProtocolConformanceKind::Inherited: {
10921069
// Substitute the base.
10931070
auto inheritedConformance
10941071
= cast<InheritedProtocolConformance>(this)->getInheritedConformance();
1095-
ProtocolConformance *newBase;
1096-
if (inheritedConformance->getType()->isSpecialized()) {
1097-
// Follow the substituted type up the superclass chain until we reach
1098-
// the underlying class type.
1099-
auto targetClass =
1100-
inheritedConformance->getType()->getClassOrBoundGenericClass();
1101-
auto superclassType = substType->getSuperclassForDecl(targetClass);
11021072

1073+
auto origType = getType();
1074+
if (!origType->hasTypeParameter() &&
1075+
!origType->hasArchetype()) {
1076+
return const_cast<ProtocolConformance *>(this);
1077+
}
1078+
1079+
auto origBaseType = inheritedConformance->getType();
1080+
if (origBaseType->hasTypeParameter() ||
1081+
origBaseType->hasArchetype()) {
11031082
// Substitute into the superclass.
1104-
newBase = inheritedConformance->subst(superclassType, subs, conformances);
1105-
} else {
1106-
newBase = inheritedConformance;
1083+
inheritedConformance = inheritedConformance->subst(subs, conformances);
11071084
}
11081085

1086+
auto substType = origType.subst(subs, conformances,
1087+
SubstFlags::UseErrorType);
11091088
return substType->getASTContext()
1110-
.getInheritedConformance(substType, newBase);
1089+
.getInheritedConformance(substType, inheritedConformance);
11111090
}
11121091
case ProtocolConformanceKind::Specialized: {
11131092
// Substitute the substitutions in the specialized conformance.
11141093
auto spec = cast<SpecializedProtocolConformance>(this);
11151094
auto genericConformance = spec->getGenericConformance();
11161095
auto subMap = spec->getSubstitutionMap();
11171096

1097+
auto origType = getType();
1098+
auto substType = origType.subst(subs, conformances,
1099+
SubstFlags::UseErrorType);
11181100
return substType->getASTContext()
11191101
.getSpecializedConformance(substType, genericConformance,
11201102
subMap.subst(subs, conformances));

lib/AST/SubstitutionMap.cpp

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -437,20 +437,44 @@ SubstitutionMap SubstitutionMap::subst(TypeSubstitutionFn subs,
437437
LookupConformanceFn conformances) const {
438438
if (empty()) return SubstitutionMap();
439439

440-
return get(
441-
getGenericSignature(),
442-
[&](SubstitutableType *type) {
443-
return Type(type).subst(*this, SubstFlags::UseErrorType)
444-
.subst(subs, conformances, SubstFlags::UseErrorType);
445-
},
446-
[&](CanType dependentType, Type replacementType,
447-
ProtocolDecl *proto) ->Optional<ProtocolConformanceRef> {
448-
auto conformance =
449-
lookupConformance(dependentType, proto)
450-
.getValueOr(ProtocolConformanceRef::forInvalid());
451-
auto substType = dependentType.subst(*this, SubstFlags::UseErrorType);
452-
return conformance.subst(substType, subs, conformances);
453-
});
440+
SmallVector<Type, 4> newSubs;
441+
for (Type type : getReplacementTypes()) {
442+
if (!type) {
443+
// Non-canonical parameter.
444+
newSubs.push_back(Type());
445+
continue;
446+
}
447+
newSubs.push_back(type.subst(subs, conformances, SubstFlags::UseErrorType));
448+
}
449+
450+
SmallVector<ProtocolConformanceRef, 4> newConformances;
451+
auto oldConformances = getConformances();
452+
453+
auto *genericSig = getGenericSignature();
454+
for (const auto &req : genericSig->getRequirements()) {
455+
if (req.getKind() != RequirementKind::Conformance) continue;
456+
457+
auto conformance = oldConformances[0];
458+
459+
// Fast path for concrete case -- we don't need to compute substType
460+
// at all.
461+
if (conformance.isConcrete()) {
462+
newConformances.push_back(
463+
ProtocolConformanceRef(
464+
conformance.getConcrete()->subst(subs, conformances)));
465+
} else {
466+
auto origType = req.getFirstType();
467+
auto substType = origType.subst(*this, SubstFlags::UseErrorType);
468+
469+
newConformances.push_back(
470+
conformance.subst(substType, subs, conformances));
471+
}
472+
473+
oldConformances = oldConformances.slice(1);
474+
}
475+
476+
assert(oldConformances.empty());
477+
return SubstitutionMap(genericSig, newSubs, newConformances);
454478
}
455479

456480
SubstitutionMap

lib/IRGen/GenProto.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,8 +1112,7 @@ getWitnessTableLazyAccessFunction(IRGenModule &IGM,
11121112
static ProtocolConformance &mapConformanceIntoContext(IRGenModule &IGM,
11131113
const ProtocolConformance &conf,
11141114
DeclContext *dc) {
1115-
return *conf.subst(dc->mapTypeIntoContext(conf.getType()),
1116-
[&](SubstitutableType *t) -> Type {
1115+
return *conf.subst([&](SubstitutableType *t) -> Type {
11171116
return dc->mapTypeIntoContext(t);
11181117
},
11191118
LookUpConformanceInModule(IGM.getSwiftModule()));

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3366,11 +3366,7 @@ void ConformanceChecker::ensureRequirementsAreSatisfied(
33663366
auto concreteConformance = conformance.getConcrete();
33673367

33683368
// Map the conformance.
3369-
auto interfaceType =
3370-
concreteConformance->getType()->mapTypeOutOfContext();
3371-
33723369
concreteConformance = concreteConformance->subst(
3373-
interfaceType,
33743370
[](SubstitutableType *type) -> Type {
33753371
if (auto *archetypeType = type->getAs<ArchetypeType>())
33763372
return archetypeType->getInterfaceType();

test/SILGen/Inputs/keypaths_multi_file_b.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,24 @@ struct A {
99
set {}
1010
}
1111
}
12+
13+
final class C<T> {
14+
var a = 0
15+
var b = 0
16+
17+
subscript(x: Int) -> Int { return x }
18+
}
19+
20+
class D<T> {
21+
var a = 0
22+
var b = 0
23+
24+
subscript(x: Int) -> Int { return x }
25+
}
26+
27+
protocol P {
28+
var a: Int { get set }
29+
var b: Int { get set }
30+
31+
subscript(x: Int) -> Int { get set }
32+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
public protocol PP {}
2+
public protocol QQ {}
3+
4+
public class AA : PP {}
5+
public class BB : AA {}
6+
7+
public struct DD<T: PP> {}
8+
9+
public struct CC<T: PP> {
10+
public var x: DD<T> { fatalError("death") }
11+
}

test/SILGen/keypaths_multi_file.swift

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1-
// RUN: %target-swift-emit-silgen -module-name keypaths -primary-file %s %S/Inputs/keypaths_multi_file_b.swift | %FileCheck %s
2-
// RUN: %target-swift-emit-silgen -module-name keypaths %s -primary-file %S/Inputs/keypaths_multi_file_b.swift | %FileCheck --check-prefix=DEFINITION %s
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-build-swift -module-name keypaths_resilient -emit-module -Xfrontend -enable-key-path-resilience %S/Inputs/keypaths_multi_file_c.swift -emit-module-path %t/keypaths_resilient.swiftmodule
4+
5+
// RUN: %target-swift-emit-silgen -module-name keypaths -primary-file %s %S/Inputs/keypaths_multi_file_b.swift -I %t | %FileCheck %s
6+
// RUN: %target-swift-emit-silgen -module-name keypaths %s -primary-file %S/Inputs/keypaths_multi_file_b.swift -I %t | %FileCheck --check-prefix=DEFINITION %s
7+
8+
import keypaths_resilient
39

410
func foo(x: Int) -> KeyPath<A, Int> {
511
switch x {
@@ -18,3 +24,22 @@ func foo(x: Int) -> KeyPath<A, Int> {
1824
// A.subscript setter
1925
// CHECK-LABEL: sil hidden_external @$S8keypaths1AVyS2icis
2026
// DEFINITION-LABEL: sil hidden @$S8keypaths1AVyS2icis
27+
28+
func bar<T>(_: T) {
29+
_ = \C<T>.b
30+
_ = \C<T>[0]
31+
32+
_ = \D<T>.b
33+
_ = \D<T>[0]
34+
35+
_ = \P.b
36+
37+
// FIXME: crashes
38+
// _ = \P[0]
39+
}
40+
41+
// https://bugs.swift.org/browse/SR-8643
42+
class MM<T: P> : PP {}
43+
func foo<T3: BB, T4: MM<T3>>(t: T3, u: T4) {
44+
let _ = \CC<T4>.x
45+
}

test/multifile/Inputs/keypath.swift

Lines changed: 0 additions & 20 deletions
This file was deleted.

test/multifile/keypath.swift

Lines changed: 0 additions & 14 deletions
This file was deleted.

0 commit comments

Comments
 (0)