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