Skip to content

Commit 2d89a97

Browse files
authored
Merge pull request #8937 from slavapestov/check-yourself-before-you-wreck-yourself
Fix a couple of dynamic Self bugs and a bonus protocol self-conformance crasher
2 parents e9355f0 + 8a0b4b0 commit 2d89a97

File tree

10 files changed

+149
-26
lines changed

10 files changed

+149
-26
lines changed

lib/AST/ProtocolConformance.cpp

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,15 +103,23 @@ ProtocolConformanceRef::subst(Type origType,
103103
auto substType = origType.subst(subs, conformances,
104104
SubstFlags::UseErrorType);
105105

106-
if (substType->isOpenedExistential())
107-
return *this;
108-
109106
// If we have a concrete conformance, we need to substitute the
110107
// conformance to apply to the new type.
111108
if (isConcrete())
112109
return ProtocolConformanceRef(
113110
getConcrete()->subst(substType, subs, conformances));
114111

112+
// Opened existentials trivially conform and do not need to go through
113+
// substitution map lookup.
114+
if (substType->isOpenedExistential())
115+
return *this;
116+
117+
// If the substituted type is an existential, we have a self-conforming
118+
// existential being substituted in place of itself. There's no
119+
// conformance information in this case, so just return.
120+
if (substType->isObjCExistentialType())
121+
return *this;
122+
115123
auto *proto = getRequirement();
116124

117125
// If the original type was an archetype, check the conformance map.
@@ -154,9 +162,7 @@ ProtocolConformanceRef::subst(Type origType,
154162
return ProtocolConformanceRef(lookupResults.front());
155163
}
156164

157-
// FIXME: Rip this out once ConformanceAccessPaths are plumbed through
158-
auto *M = proto->getParentModule();
159-
return *M->lookupConformance(substType, proto, nullptr);
165+
llvm_unreachable("Invalid conformance substitution");
160166
}
161167

162168
Type
@@ -788,11 +794,16 @@ ProtocolConformance *
788794
ProtocolConformance::subst(Type substType,
789795
TypeSubstitutionFn subs,
790796
LookupConformanceFn conformances) const {
797+
// ModuleDecl::lookupConformance() strips off dynamic Self, so
798+
// we should do the same here.
799+
if (auto selfType = substType->getAs<DynamicSelfType>())
800+
substType = selfType->getSelfType();
801+
791802
if (getType()->isEqual(substType))
792803
return const_cast<ProtocolConformance *>(this);
793804

794805
switch (getKind()) {
795-
case ProtocolConformanceKind::Normal:
806+
case ProtocolConformanceKind::Normal: {
796807
if (substType->isSpecialized()) {
797808
assert(getType()->isSpecialized()
798809
&& "substitution mapped non-specialized to specialized?!");
@@ -831,10 +842,11 @@ ProtocolConformance::subst(Type substType,
831842
const_cast<ProtocolConformance *>(this),
832843
subMap);
833844
}
845+
834846
assert(substType->isEqual(getType())
835847
&& "substitution changed non-specialized type?!");
836848
return const_cast<ProtocolConformance *>(this);
837-
849+
}
838850
case ProtocolConformanceKind::Inherited: {
839851
// Substitute the base.
840852
auto inheritedConformance

lib/AST/SubstitutionMap.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,8 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const {
124124
if (!genericSig->conformsToProtocol(type, proto, mod))
125125
return None;
126126

127-
auto canonType = genericSig->getCanonicalTypeInContext(type, mod);
128127
auto accessPath =
129-
genericSig->getConformanceAccessPath(canonType, proto, mod);
128+
genericSig->getConformanceAccessPath(type, proto, mod);
130129

131130
// Fall through because we cannot yet evaluate an access path.
132131
Optional<ProtocolConformanceRef> conformance;
@@ -143,8 +142,21 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const {
143142
// If we've hit an abstract conformance, everything from here on out is
144143
// abstract.
145144
// FIXME: This may not always be true, but it holds for now.
146-
if (conformance->isAbstract())
145+
if (conformance->isAbstract()) {
146+
// FIXME: Rip this out once we can get a concrete conformance from
147+
// an archetype.
148+
auto *M = proto->getParentModule();
149+
auto substType = type.subst(*this);
150+
if (substType &&
151+
!substType->is<ArchetypeType>() &&
152+
!substType->isTypeParameter() &&
153+
!substType->isExistentialType()) {
154+
auto lazyResolver = M->getASTContext().getLazyResolver();
155+
return *M->lookupConformance(substType, proto, lazyResolver);
156+
}
157+
147158
return ProtocolConformanceRef(proto);
159+
}
148160

149161
// For the second step, we're looking into the requirement signature for
150162
// this protocol.
@@ -153,7 +165,7 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const {
153165

154166
// If we haven't set the signature conformances yet, force the issue now.
155167
if (normal->getSignatureConformances().empty()) {
156-
auto lazyResolver = canonType->getASTContext().getLazyResolver();
168+
auto lazyResolver = type->getASTContext().getLazyResolver();
157169
lazyResolver->resolveTypeWitness(normal, nullptr);
158170

159171
// Error case: the conformance is broken, so we cannot handle this

lib/AST/Type.cpp

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2946,17 +2946,7 @@ Optional<ProtocolConformanceRef>
29462946
LookUpConformanceInSubstitutionMap::operator()(CanType dependentType,
29472947
Type conformingReplacementType,
29482948
ProtocolType *conformedProtocol) const {
2949-
auto result = Subs.lookupConformance(dependentType, conformedProtocol->getDecl());
2950-
if ((result && result->isConcrete()) ||
2951-
conformingReplacementType->hasError() ||
2952-
conformingReplacementType->isTypeParameter())
2953-
return result;
2954-
2955-
// FIXME: Rip this out once ConformanceAccessPaths are plumbed through
2956-
auto *M = conformedProtocol->getDecl()->getParentModule();
2957-
return M->lookupConformance(conformingReplacementType,
2958-
conformedProtocol->getDecl(),
2959-
nullptr);
2949+
return Subs.lookupConformance(dependentType, conformedProtocol->getDecl());
29602950
}
29612951

29622952
Optional<ProtocolConformanceRef>

lib/SILOptimizer/IPO/ClosureSpecializer.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,15 @@ void SILClosureSpecializerTransform::gatherCallSites(
771771
!ApplyCallee->hasValidLinkageForFragileInline())
772772
continue;
773773

774+
// If the callee uses a dynamic Self, we cannot specialize it,
775+
// since the resulting specialization might longer has 'self' as the
776+
// last parameter.
777+
//
778+
// We could fix this by inserting new arguments more carefully, or
779+
// changing how we model dynamic Self altogether.
780+
if (mayBindDynamicSelf(ApplyCallee))
781+
return;
782+
774783
// Ok, we know that we can perform the optimization but not whether or
775784
// not the optimization is profitable. Find the index of the argument
776785
// corresponding to our partial apply.

stdlib/public/core/KeyPath.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,11 @@ public class AnyKeyPath: Hashable, _AppendKeyPath {
114114
_abstract()
115115
}
116116

117-
// FIXME: This should return Self, but that breaks the closure specializer.
118-
// rdar://problem/31725007
119117
public // @testable
120118
static func _create(
121119
capacityInBytes bytes: Int,
122120
initializedBy body: (UnsafeMutableRawBufferPointer) -> Void
123-
) -> AnyKeyPath {
121+
) -> Self {
124122
_sanityCheck(bytes > 0 && bytes % 4 == 0,
125123
"capacity must be multiple of 4 bytes")
126124
let result = Builtin.allocWithTailElems_1(self, (bytes/4)._builtinWordValue,
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %target-swift-frontend -emit-sil -O -primary-file %s
2+
3+
// Just make sure we skip the optimization and not crash here.
4+
//
5+
// Eventually, we can make this work.
6+
//
7+
// <rdar://problem/31725007>
8+
9+
class Foo {
10+
required init() {}
11+
12+
static func foo(_ f: () -> ()) -> Self {
13+
f()
14+
return self.init()
15+
}
16+
}
17+
18+
class Bar: Foo {}
19+
20+
func closures(_ x: String) {
21+
print(Foo.foo { _ = x })
22+
print(Bar.foo { _ = x })
23+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %target-swift-frontend -emit-sil -O -primary-file %s | %FileCheck %s
2+
3+
protocol P {}
4+
5+
extension P {
6+
@_semantics("optimize.sil.never") func method1() {}
7+
8+
@inline(__always) func method2() { method1() }
9+
}
10+
11+
class C<T> : P {
12+
// CHECK-LABEL: sil shared [always_inline] @_T023specialize_dynamic_self1CC11returnsSelfACyxGXDyFSi_Tg5 : $@convention(method) (@guaranteed C<Int>) -> @owned C<Int>
13+
// CHECK: [[RESULT:%.*]] = alloc_stack $C<Int>
14+
// CHECK: [[FN:%.*]] = function_ref @_T023specialize_dynamic_self1PPAAE7method1yyF : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
15+
// CHECK: apply [[FN]]<@dynamic_self C<Int>>([[RESULT]]) : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
16+
// CHECK: return %0 : $C<Int>
17+
@inline(__always)
18+
final func returnsSelf() -> Self {
19+
method2()
20+
return self
21+
}
22+
}
23+
24+
// CHECK-LABEL: sil hidden [thunk] [always_inline] @_T023specialize_dynamic_self8usesCIntyAA1CCySiG1c_tF : $@convention(thin) (@owned C<Int>) -> () {
25+
// CHECK: function_ref @_T023specialize_dynamic_self1CC11returnsSelfACyxGXDyFSi_Tg5 : $@convention(method) (@guaranteed C<Int>) -> @owned C<Int>
26+
// CHECK: return
27+
func usesCInt(c: C<Int>) {
28+
_ = c.returnsSelf()
29+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %target-swift-frontend -emit-sil -O -primary-file %s | %FileCheck %s
2+
3+
// REQUIRES: objc_interop
4+
5+
import Foundation
6+
7+
@objc protocol P {}
8+
9+
@_semantics("optimize.sil.never")
10+
func takesP<T : P>(_: T) {}
11+
12+
@inline(__always)
13+
func callsTakesP<T : P>(_ t: T) {
14+
takesP(t)
15+
}
16+
17+
// CHECK-LABEL: sil hidden @_T026specialize_self_conforming16callsTakesPWithPyAA1P_pF : $@convention(thin) (@owned P) -> () {
18+
// CHECK: [[FN:%.*]] = function_ref @_T026specialize_self_conforming6takesPyxAA1PRzlF : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned τ_0_0) -> ()
19+
// CHECK: apply [[FN]]<P>(%0) : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned τ_0_0) -> ()
20+
// CHECK: return
21+
22+
func callsTakesPWithP(_ p: P) {
23+
callsTakesP(p)
24+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// RUN: not --crash %target-swift-frontend -emit-ir -primary-file %s
2+
3+
class Base<T: AnyObject> {}
4+
5+
class Derived: Base<Derived> {}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
// RUN: %target-build-swift %s -o %t/a.out
4+
// RUN: not --crash %target-run %t/a.out
5+
6+
// REQUIRES: executable_test
7+
// REQUIRES: OS=macosx
8+
9+
// "cyclic metadata dependency detected, aborting"
10+
11+
struct X<T> {
12+
enum S {
13+
case some(T), none
14+
}
15+
16+
init() { a = .none }
17+
var a: S
18+
}
19+
20+
X<()>()
21+

0 commit comments

Comments
 (0)