14
14
#include " swift/AST/ASTContext.h"
15
15
#include " swift/AST/Decl.h"
16
16
#include " swift/AST/DiagnosticsFrontend.h"
17
+ #include " swift/AST/ExistentialLayout.h"
17
18
#include " swift/AST/FileSystem.h"
18
19
#include " swift/AST/Module.h"
19
20
#include " swift/Frontend/Frontend.h"
@@ -431,6 +432,157 @@ static void printImports(raw_ostream &out, ModuleDecl *M) {
431
432
}
432
433
}
433
434
435
+ // FIXME: Copied from ASTPrinter.cpp...
436
+ static bool isPublicOrUsableFromInline (const ValueDecl *VD) {
437
+ AccessScope scope =
438
+ VD->getFormalAccessScope (/* useDC*/ nullptr ,
439
+ /* treatUsableFromInlineAsPublic*/ true );
440
+ return scope.isPublic ();
441
+ }
442
+
443
+ static bool isPublicOrUsableFromInline (Type ty) {
444
+ // Note the double negative here: we're looking for any referenced decls that
445
+ // are *not* public-or-usableFromInline.
446
+ return !ty.findIf ([](Type typePart) -> bool {
447
+ // FIXME: If we have an internal typealias for a non-internal type, we ought
448
+ // to be able to print it by desugaring.
449
+ if (auto *aliasTy = dyn_cast<NameAliasType>(typePart.getPointer ()))
450
+ return !isPublicOrUsableFromInline (aliasTy->getDecl ());
451
+ if (auto *nominal = typePart->getAnyNominal ())
452
+ return !isPublicOrUsableFromInline (nominal);
453
+ return false ;
454
+ });
455
+ }
456
+
457
+ namespace {
458
+ // / Collects protocols that are conformed to by a particular nominal. Since
459
+ // / ASTPrinter will only print the public ones, the non-public ones get left by
460
+ // / the wayside. This is a problem when a non-public protocol inherits from a
461
+ // / public protocol; the generated parseable interface still needs to make that
462
+ // / dependency public.
463
+ // /
464
+ // / The solution implemented here is to generate synthetic extensions that
465
+ // / declare the extra conformances. This isn't perfect (it loses the sugared
466
+ // / spelling of the protocol type, as well as the locality in the file), but it
467
+ // / does work.
468
+ class InheritedProtocolCollector {
469
+ // / Protocols that will be included by the ASTPrinter without any extra work.
470
+ SmallVector<ProtocolDecl *, 8 > IncludedProtocols;
471
+ // / Protocols that will not be printed by the ASTPrinter.
472
+ SmallVector<ProtocolDecl *, 8 > ExtraProtocols;
473
+
474
+ // / For each type in \p directlyInherited, classify the protocols it refers to
475
+ // / as included for printing or not, and record them in the appropriate
476
+ // / vectors.
477
+ void recordProtocols (ArrayRef<TypeLoc> directlyInherited) {
478
+ for (TypeLoc inherited : directlyInherited) {
479
+ Type inheritedTy = inherited.getType ();
480
+ if (!inheritedTy || !inheritedTy->isExistentialType ())
481
+ continue ;
482
+
483
+ bool canPrintNormally = isPublicOrUsableFromInline (inheritedTy);
484
+ SmallVectorImpl<ProtocolDecl *> &whichProtocols =
485
+ canPrintNormally ? IncludedProtocols : ExtraProtocols;
486
+
487
+ ExistentialLayout layout = inheritedTy->getExistentialLayout ();
488
+ for (ProtocolType *protoTy : layout.getProtocols ())
489
+ whichProtocols.push_back (protoTy->getDecl ());
490
+ // FIXME: This ignores layout constraints, but currently we don't support
491
+ // any of those besides 'AnyObject'.
492
+ }
493
+ }
494
+
495
+ public:
496
+ using PerTypeMap = llvm::MapVector<const NominalTypeDecl *,
497
+ InheritedProtocolCollector>;
498
+
499
+ // / Given that we're about to print \p D, record its protocols in \p map.
500
+ // /
501
+ // / \sa recordProtocols
502
+ static void collectProtocols (PerTypeMap &map, const Decl *D) {
503
+ ArrayRef<TypeLoc> directlyInherited;
504
+ const NominalTypeDecl *nominal;
505
+ const IterableDeclContext *memberContext;
506
+
507
+ if ((nominal = dyn_cast<NominalTypeDecl>(D))) {
508
+ directlyInherited = nominal->getInherited ();
509
+ memberContext = nominal;
510
+
511
+ } else if (auto *extension = dyn_cast<ExtensionDecl>(D)) {
512
+ if (extension->isConstrainedExtension ()) {
513
+ // Conditional conformances never apply to inherited protocols, nor
514
+ // can they provide unconditional conformances that might be used in
515
+ // other extensions.
516
+ return ;
517
+ }
518
+ nominal = extension->getExtendedNominal ();
519
+ directlyInherited = extension->getInherited ();
520
+ memberContext = extension;
521
+
522
+ } else {
523
+ return ;
524
+ }
525
+
526
+ map[nominal].recordProtocols (directlyInherited);
527
+
528
+ // Recurse to find any nested types.
529
+ for (const Decl *member : memberContext->getMembers ())
530
+ collectProtocols (map, member);
531
+ }
532
+
533
+ // / If there were any public protocols that need to be printed (i.e. they
534
+ // / weren't conformed to explicitly or inherited by another printed protocol),
535
+ // / do so now by printing a dummy extension on \p nominal to \p out.
536
+ void
537
+ printSynthesizedExtensionIfNeeded (raw_ostream &out,
538
+ const PrintOptions &printOptions,
539
+ const NominalTypeDecl *nominal) const {
540
+ if (ExtraProtocols.empty ())
541
+ return ;
542
+
543
+ SmallPtrSet<ProtocolDecl *, 16 > handledProtocols;
544
+
545
+ // First record all protocols that have already been handled.
546
+ for (ProtocolDecl *proto : IncludedProtocols) {
547
+ proto->walkInheritedProtocols (
548
+ [&handledProtocols](ProtocolDecl *inherited) -> TypeWalker::Action {
549
+ handledProtocols.insert (inherited);
550
+ return TypeWalker::Action::Continue;
551
+ });
552
+ }
553
+
554
+ // Then walk the remaining ones, and see what we need to print.
555
+ // Note: We could do this in one pass, but the logic is easier to
556
+ // understand if we build up the list and then print it, even if it takes
557
+ // a bit more memory.
558
+ SmallVector<ProtocolDecl *, 16 > protocolsToPrint;
559
+ for (ProtocolDecl *proto : ExtraProtocols) {
560
+ proto->walkInheritedProtocols (
561
+ [&](ProtocolDecl *inherited) -> TypeWalker::Action {
562
+ if (!handledProtocols.insert (inherited).second )
563
+ return TypeWalker::Action::SkipChildren;
564
+ if (isPublicOrUsableFromInline (inherited)) {
565
+ protocolsToPrint.push_back (inherited);
566
+ return TypeWalker::Action::SkipChildren;
567
+ }
568
+ return TypeWalker::Action::Continue;
569
+ });
570
+ }
571
+ if (protocolsToPrint.empty ())
572
+ return ;
573
+
574
+ out << " extension " ;
575
+ nominal->getDeclaredType ().print (out, printOptions);
576
+ out << " : " ;
577
+ swift::interleave (protocolsToPrint,
578
+ [&out, &printOptions](ProtocolDecl *proto) {
579
+ proto->getDeclaredType ()->print (out, printOptions);
580
+ }, [&out] { out << " , " ; });
581
+ out << " {}\n " ;
582
+ }
583
+ };
584
+ } // end anonymous namespace
585
+
434
586
bool swift::emitParseableInterface (raw_ostream &out,
435
587
ParseableInterfaceOptions const &Opts,
436
588
ModuleDecl *M) {
@@ -440,13 +592,26 @@ bool swift::emitParseableInterface(raw_ostream &out,
440
592
printImports (out, M);
441
593
442
594
const PrintOptions printOptions = PrintOptions::printParseableInterfaceFile ();
595
+ InheritedProtocolCollector::PerTypeMap inheritedProtocolMap;
596
+
443
597
SmallVector<Decl *, 16 > topLevelDecls;
444
598
M->getTopLevelDecls (topLevelDecls);
445
599
for (const Decl *D : topLevelDecls) {
446
600
if (!D->shouldPrintInContext (printOptions))
447
601
continue ;
602
+
448
603
D->print (out, printOptions);
449
604
out << " \n " ;
605
+
606
+ InheritedProtocolCollector::collectProtocols (inheritedProtocolMap, D);
607
+ }
608
+
609
+ // Print dummy extensions for any protocols that were indirectly conformed to.
610
+ for (const auto &nominalAndCollector : inheritedProtocolMap) {
611
+ const InheritedProtocolCollector &collector = nominalAndCollector.second ;
612
+ collector.printSynthesizedExtensionIfNeeded (out, printOptions,
613
+ nominalAndCollector.first );
450
614
}
615
+
451
616
return false ;
452
617
}
0 commit comments