Skip to content

Commit b545e19

Browse files
committed
ConformanceLookupTable: Recursively inherit into subclasses when incrementally adding extension conformances.
Previously, we would only reliably propagate conformances from new extensions to immediate subclasses, since when we visit grandchild classes, we'd see no change in the immediate base class's status. Fix this by walking up the entire superclass chain when we look for new inherited conformances, and track the last processed state of different nominal type decls' extensions separately. Fixes SR-1480.
1 parent 7154c61 commit b545e19

10 files changed

+124
-27
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3203,6 +3203,10 @@ class ClassDecl : public NominalTypeDecl {
32033203
/// Retrieve the superclass of this class, or null if there is no superclass.
32043204
Type getSuperclass() const { return LazySemanticInfo.Superclass.getPointer(); }
32053205

3206+
/// Retrieve the ClassDecl for the superclass of this class, or null if there
3207+
/// is no superclass.
3208+
ClassDecl *getSuperclassDecl() const;
3209+
32063210
/// Set the superclass of this class.
32073211
void setSuperclass(Type superclass) {
32083212
LazySemanticInfo.Superclass.setPointerAndInt(superclass, true);

include/swift/AST/TypeAlignments.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ namespace swift {
3333
class Decl;
3434
class DeclContext;
3535
class Expr;
36+
class ExtensionDecl;
3637
class GenericTypeParamDecl;
3738
class NormalProtocolConformance;
3839
class OperatorDecl;
@@ -80,6 +81,7 @@ LLVM_DECLARE_TYPE_ALIGNMENT(swift::GenericTypeParamDecl, swift::DeclAlignInBits)
8081
LLVM_DECLARE_TYPE_ALIGNMENT(swift::OperatorDecl, swift::DeclAlignInBits)
8182
LLVM_DECLARE_TYPE_ALIGNMENT(swift::ProtocolDecl, swift::DeclAlignInBits)
8283
LLVM_DECLARE_TYPE_ALIGNMENT(swift::ValueDecl, swift::DeclAlignInBits)
84+
LLVM_DECLARE_TYPE_ALIGNMENT(swift::ExtensionDecl, swift::DeclAlignInBits)
8385

8486
LLVM_DECLARE_TYPE_ALIGNMENT(swift::TypeBase, swift::TypeAlignInBits)
8587
LLVM_DECLARE_TYPE_ALIGNMENT(swift::ArchetypeType, swift::TypeAlignInBits)

lib/AST/ConformanceLookupTable.cpp

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,10 @@ void ConformanceLookupTable::forEachInStage(ConformanceStage stage,
141141
ExtensionFunc extensionFunc) {
142142
assert(static_cast<unsigned>(stage) < NumConformanceStages &&
143143
"NumConformanceStages has not been updated");
144-
LastProcessedEntry &lastProcessed
145-
= LastProcessed[static_cast<unsigned>(stage)];
146144

145+
LastProcessedEntry &lastProcessed
146+
= LastProcessed[nominal][static_cast<unsigned>(stage)];
147+
147148
// Handle the nominal type.
148149
if (!lastProcessed.getInt()) {
149150
lastProcessed.setInt(true);
@@ -300,23 +301,26 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal,
300301
if (resolver)
301302
resolver->resolveSuperclass(classDecl);
302303

303-
if (Type superclass = classDecl->getSuperclass()) {
304-
if (auto superclassDecl
305-
= superclass->getClassOrBoundGenericClass()) {
306-
// Break infinite recursion when visiting ill-formed classes
307-
// with circular inheritance.
308-
if (VisitingSuperclass)
309-
return;
310-
llvm::SaveAndRestore<bool> visiting(VisitingSuperclass, true);
311-
312-
// Resolve the conformances of the superclass.
313-
superclassDecl->prepareConformanceTable();
314-
superclassDecl->ConformanceTable->updateLookupTable(
315-
superclassDecl,
316-
ConformanceStage::Resolved,
317-
resolver);
318-
319-
// Expand inherited conformances.
304+
if (auto superclassDecl = classDecl->getSuperclassDecl()) {
305+
// Break infinite recursion when visiting ill-formed classes
306+
// with circular inheritance.
307+
if (VisitingSuperclass)
308+
return;
309+
llvm::SaveAndRestore<bool> visiting(VisitingSuperclass, true);
310+
311+
// Resolve the conformances of the superclass.
312+
superclassDecl->prepareConformanceTable();
313+
superclassDecl->ConformanceTable->updateLookupTable(
314+
superclassDecl,
315+
ConformanceStage::Resolved,
316+
resolver);
317+
318+
// Expand inherited conformances from all superclasses.
319+
// We may have circular inheritance in ill-formed classes, so keep an
320+
// eye out for that.
321+
auto circularSuperclass = superclassDecl->getSuperclassDecl();
322+
323+
do {
320324
forEachInStage(stage, superclassDecl, resolver,
321325
[&](NominalTypeDecl *superclass) {
322326
inheritConformances(classDecl, superclassDecl,
@@ -326,7 +330,12 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal,
326330
inheritConformances(classDecl, superclassDecl, ext,
327331
resolver);
328332
});
329-
}
333+
superclassDecl = superclassDecl->getSuperclassDecl();
334+
if (circularSuperclass)
335+
circularSuperclass = circularSuperclass->getSuperclassDecl();
336+
if (circularSuperclass)
337+
circularSuperclass = circularSuperclass->getSuperclassDecl();
338+
} while (superclassDecl != circularSuperclass);
330339
}
331340
}
332341
break;
@@ -742,11 +751,11 @@ DeclContext *ConformanceLookupTable::getConformingContext(
742751
return nullptr;
743752

744753
// If we had a circular dependency, the superclass may not exist.
745-
if (!classDecl->getSuperclass())
746-
return nullptr;
747-
748754
auto superclassDecl
749-
= classDecl->getSuperclass()->getClassOrBoundGenericClass();
755+
= classDecl->getSuperclassDecl();
756+
757+
if (!superclassDecl)
758+
return nullptr;
750759

751760
if (!classDecl->ConformanceTable->VisitingSuperclass) {
752761
llvm::SaveAndRestore<bool> visiting(

lib/AST/ConformanceLookupTable.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "llvm/ADT/MapVector.h"
2828
#include "llvm/ADT/PointerUnion.h"
2929
#include "llvm/ADT/SetVector.h"
30+
#include <unordered_map>
3031

3132
namespace swift {
3233

@@ -67,11 +68,16 @@ class ConformanceLookupTable {
6768
/// at that stage.
6869
typedef llvm::PointerIntPair<ExtensionDecl *, 1, bool> LastProcessedEntry;
6970

70-
/// Array indicating how far we have gotten in processing the
71+
/// Array indicating how far we have gotten in processing each
7172
/// nominal type and list of extensions for each stage of
7273
/// conformance checking.
73-
LastProcessedEntry LastProcessed[NumConformanceStages];
74-
74+
///
75+
/// Uses std::unordered_map instead of DenseMap so that stable interior
76+
/// references can be taken.
77+
std::unordered_map<NominalTypeDecl *,
78+
std::array<LastProcessedEntry, NumConformanceStages>>
79+
LastProcessed;
80+
7581
/// The list of parsed extension declarations that have been delayed because
7682
/// no resolver was available at the time.
7783
///

lib/AST/Decl.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4677,3 +4677,9 @@ Type TypeBase::getSwiftNewtypeUnderlyingType() {
46774677

46784678
return {};
46794679
}
4680+
4681+
ClassDecl *ClassDecl::getSuperclassDecl() const {
4682+
if (auto superclass = getSuperclass())
4683+
return superclass->getClassOrBoundGenericClass();
4684+
return nullptr;
4685+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
extension Responder {}
2+
extension View {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
__attribute__((objc_root_class))
2+
@interface Object
3+
+alloc;
4+
-init;
5+
@end
6+
7+
@interface Responder: Object
8+
@end
9+
10+
@interface View: Responder
11+
@end
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %target-swift-frontend -parse -verify %s
2+
3+
class Object {}
4+
class Responder: Object {}
5+
class View: Responder {}
6+
7+
extension View {}
8+
9+
protocol Foo {}
10+
11+
extension Foo {
12+
func bar() -> Self { return self }
13+
}
14+
15+
extension Object: Foo {}
16+
17+
_ = Object().bar()
18+
_ = Responder().bar()
19+
_ = View().bar()
20+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %target-swift-frontend -import-objc-header %S/Inputs/extension-inheritance-conformance-objc.h -parse -verify -primary-file %s %S/Inputs/extension-inheritance-conformance-objc-multi-file-2.swift
2+
// RUN: %target-swift-frontend -import-objc-header %S/Inputs/extension-inheritance-conformance-objc.h -parse -verify %s -primary-file %S/Inputs/extension-inheritance-conformance-objc-multi-file-2.swift
3+
// RUN: %target-swift-frontend -import-objc-header %S/Inputs/extension-inheritance-conformance-objc.h -parse -verify -primary-file %S/Inputs/extension-inheritance-conformance-objc-multi-file-2.swift %s
4+
// RUN: %target-swift-frontend -import-objc-header %S/Inputs/extension-inheritance-conformance-objc.h -parse -verify %S/Inputs/extension-inheritance-conformance-objc-multi-file-2.swift -primary-file %s
5+
6+
// REQUIRES: objc_interop
7+
8+
protocol Foo {}
9+
10+
extension Foo {
11+
func bar() -> Self { return self }
12+
}
13+
14+
extension Object: Foo {}
15+
16+
let x = Object().bar()
17+
let y = Responder().bar()
18+
let z = View().bar()
19+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: %target-swift-frontend -import-objc-header %S/Inputs/extension-inheritance-conformance-objc.h -parse -verify %s
2+
3+
// REQUIRES: objc_interop
4+
5+
extension View {}
6+
7+
protocol Foo {}
8+
9+
extension Foo {
10+
func bar() -> Self { return self }
11+
}
12+
13+
extension Object: Foo {}
14+
15+
_ = Object().bar()
16+
_ = Responder().bar()
17+
_ = View().bar()
18+

0 commit comments

Comments
 (0)