@@ -1865,6 +1865,9 @@ applyPropertyOwnership(VarDecl *prop,
1865
1865
}
1866
1866
}
1867
1867
1868
+ using MirroredMethodEntry =
1869
+ std::pair<const clang::ObjCMethodDecl*, ProtocolDecl*>;
1870
+
1868
1871
namespace {
1869
1872
// / Customized llvm::DenseMapInfo for storing borrowed APSInts.
1870
1873
struct APSIntRefDenseMapInfo {
@@ -3934,6 +3937,10 @@ namespace {
3934
3937
SmallVectorImpl<Decl *> &members,
3935
3938
ASTContext &Ctx);
3936
3939
3940
+ void importNonOverriddenMirroredMethods (DeclContext *dc,
3941
+ MutableArrayRef<MirroredMethodEntry> entries,
3942
+ SmallVectorImpl<Decl *> &newMembers);
3943
+
3937
3944
// / \brief Import constructors from our superclasses (and their
3938
3945
// / categories/extensions), effectively "inheriting" constructors.
3939
3946
void importInheritedConstructors (ClassDecl *classDecl,
@@ -6405,6 +6412,15 @@ void SwiftDeclConverter::importMirroredProtocolMembers(
6405
6412
const ClangModuleUnit *declModule;
6406
6413
const ClangModuleUnit *interfaceModule;
6407
6414
6415
+ // 'protocols' is, for some reason, the full recursive expansion of
6416
+ // the protocol hierarchy, so there's no need to recursively descend
6417
+ // into inherited protocols.
6418
+
6419
+ // Try to import only the most specific methods with a particular name.
6420
+ // We use a MapVector to get deterministic iteration order later.
6421
+ llvm::MapVector<clang::Selector, std::vector<MirroredMethodEntry>>
6422
+ methodsByName;
6423
+
6408
6424
for (auto proto : protocols) {
6409
6425
auto clangProto =
6410
6426
cast_or_null<clang::ObjCProtocolDecl>(proto->getClangDecl ());
@@ -6493,27 +6509,143 @@ void SwiftDeclConverter::importMirroredProtocolMembers(
6493
6509
if (!objcMethod)
6494
6510
continue ;
6495
6511
6496
- // When mirroring an initializer, make it designated and required.
6497
- if (isInitMethod (objcMethod)) {
6498
- // Import the constructor.
6499
- if (auto imported = importConstructor (objcMethod, dc, /* implicit=*/ true ,
6500
- CtorInitializerKind::Designated,
6501
- /* required=*/ true )) {
6502
- members.push_back (imported);
6503
- }
6512
+ // For now, just remember that we saw this method.
6513
+ methodsByName[objcMethod->getSelector ()]
6514
+ .push_back (MirroredMethodEntry{objcMethod, proto});
6515
+ }
6516
+ }
6504
6517
6505
- continue ;
6506
- }
6518
+ // Process all the methods, now that we've arranged them by selector.
6519
+ for (auto &mapEntry : methodsByName) {
6520
+ importNonOverriddenMirroredMethods (dc, mapEntry.second , members);
6521
+ }
6522
+ }
6507
6523
6508
- // Import the method.
6509
- if (auto imported =
6510
- Impl.importMirroredDecl (objcMethod, dc, getVersion (), proto)) {
6511
- members.push_back (imported);
6524
+ enum MirrorImportComparison {
6525
+ // There's no suppression relationship between the methods.
6526
+ NoSuppression,
6527
+
6528
+ // The first method suppresses the second.
6529
+ Suppresses,
6530
+
6531
+ // The second method suppresses the first.
6532
+ IsSuppressed,
6533
+ };
6534
+
6535
+ // / Should the mirror import of the first method be suppressed in favor
6536
+ // / of the second method? The methods are known to have the same selector
6537
+ // / and (because this is mirror-import) to be declared on protocols.
6538
+ // /
6539
+ // / The algorithm that uses this assumes that it is transitive.
6540
+ static bool isMirrorImportSuppressedBy (ClangImporter::Implementation &importer,
6541
+ const clang::ObjCMethodDecl *first,
6542
+ const clang::ObjCMethodDecl *second) {
6543
+ auto firstProto = cast<clang::ObjCProtocolDecl>(first->getDeclContext ());
6544
+ auto secondProto = cast<clang::ObjCProtocolDecl>(second->getDeclContext ());
6545
+
6546
+ // If the first method's protocol is a super-protocol of the second's,
6547
+ // then the second method overrides the first and we should suppress.
6548
+ // Clang provides a function to check that, phrased in terms of whether
6549
+ // a value of one protocol (the RHS) can be assigned to an l-value of
6550
+ // the other (the LHS).
6551
+ auto &ctx = importer.getClangASTContext ();
6552
+ return ctx.ProtocolCompatibleWithProtocol (
6553
+ const_cast <clang::ObjCProtocolDecl*>(firstProto),
6554
+ const_cast <clang::ObjCProtocolDecl*>(secondProto));
6555
+ }
6556
+
6557
+ // / Compare two methods for mirror-import purposes.
6558
+ static MirrorImportComparison
6559
+ compareMethodsForMirrorImport (ClangImporter::Implementation &importer,
6560
+ const clang::ObjCMethodDecl *first,
6561
+ const clang::ObjCMethodDecl *second) {
6562
+ if (isMirrorImportSuppressedBy (importer, first, second))
6563
+ return IsSuppressed;
6564
+ if (isMirrorImportSuppressedBy (importer, second, first))
6565
+ return Suppresses;
6566
+ return NoSuppression;
6567
+ }
6568
+
6569
+ // / Mark any methods in the given array that are overridden by this method
6570
+ // / as suppressed by nulling their entries out.
6571
+ // / Return true if this method is overridden by any methods in the array.
6572
+ static bool suppressOverriddenMethods (ClangImporter::Implementation &importer,
6573
+ const clang::ObjCMethodDecl *method,
6574
+ MutableArrayRef<MirroredMethodEntry> entries) {
6575
+ assert (method && " method was already suppressed" );
6576
+
6577
+ for (auto &entry: entries) {
6578
+ auto otherMethod = entry.first ;
6579
+ if (!otherMethod) continue ;
6580
+
6581
+ assert (method != otherMethod && " found same method twice?" );
6582
+ switch (compareMethodsForMirrorImport (importer, method, otherMethod)) {
6583
+ // If the second method is suppressed, null it out.
6584
+ case Suppresses:
6585
+ entry.first = nullptr ;
6586
+ continue ;
6587
+
6588
+ // If the first method is suppressed, return immediately. We should
6589
+ // be able to suppress any following methods.
6590
+ case IsSuppressed:
6591
+ return true ;
6592
+
6593
+ case NoSuppression:
6594
+ continue ;
6595
+ }
6596
+ llvm_unreachable (" bad comparison result" );
6597
+ }
6598
+
6599
+ return false ;
6600
+ }
6601
+
6602
+ // / Given a set of methods with the same selector, each taken from a
6603
+ // / different protocol in the protocol hierarchy of a class into which
6604
+ // / we want to introduce mirror imports, import only the methods which
6605
+ // / are not overridden by another method in the set.
6606
+ // /
6607
+ // / It's possible that we'll end up selecting multiple methods to import
6608
+ // / here, in the cases where there's no hierarchical relationship between
6609
+ // / two methods. The importer already has code to handle this case.
6610
+ void SwiftDeclConverter::importNonOverriddenMirroredMethods (DeclContext *dc,
6611
+ MutableArrayRef<MirroredMethodEntry> entries,
6612
+ SmallVectorImpl<Decl *> &members) {
6613
+ for (size_t i = 0 , e = entries.size (); i != e; ++i) {
6614
+ auto objcMethod = entries[i].first ;
6615
+
6616
+ // If the method was suppressed by a previous method, ignore it.
6617
+ if (!objcMethod)
6618
+ continue ;
6619
+
6620
+ // Compare this method to all the following methods, suppressing any
6621
+ // that it overrides. If it is overridden by any of them, suppress it
6622
+ // instead; but there's no need to mark that in the array, just continue
6623
+ // on to the next method.
6624
+ if (suppressOverriddenMethods (Impl, objcMethod, entries.slice (i + 1 )))
6625
+ continue ;
6626
+
6627
+ // Okay, the method wasn't suppressed, import it.
6512
6628
6513
- for (auto alternate : Impl.getAlternateDecls (imported))
6514
- if (imported->getDeclContext () == alternate->getDeclContext ())
6515
- members.push_back (alternate);
6629
+ // When mirroring an initializer, make it designated and required.
6630
+ if (isInitMethod (objcMethod)) {
6631
+ // Import the constructor.
6632
+ if (auto imported = importConstructor (objcMethod, dc, /* implicit=*/ true ,
6633
+ CtorInitializerKind::Designated,
6634
+ /* required=*/ true )) {
6635
+ members.push_back (imported);
6516
6636
}
6637
+ continue ;
6638
+ }
6639
+
6640
+ // Import the method.
6641
+ auto proto = entries[i].second ;
6642
+ if (auto imported =
6643
+ Impl.importMirroredDecl (objcMethod, dc, getVersion (), proto)) {
6644
+ members.push_back (imported);
6645
+
6646
+ for (auto alternate : Impl.getAlternateDecls (imported))
6647
+ if (imported->getDeclContext () == alternate->getDeclContext ())
6648
+ members.push_back (alternate);
6517
6649
}
6518
6650
}
6519
6651
}
0 commit comments