Skip to content

Commit cc0627a

Browse files
authored
Merge pull request #42276 from tshortli/global-actor-broken-swift-interface
ModuleInterface: Wrap synthesized extensions in swiftinterfaces with feature guards
2 parents 57f0e67 + f27005b commit cc0627a

File tree

4 files changed

+82
-63
lines changed

4 files changed

+82
-63
lines changed

include/swift/AST/PrintOptions.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,12 @@ struct PrintOptions {
391391
/// Whether to use an empty line to separate two members in a single decl.
392392
bool EmptyLineBetweenMembers = false;
393393

394+
/// Whether to print empty members of a declaration on a single line, e.g.:
395+
/// ```
396+
/// extension Foo: Bar {}
397+
/// ```
398+
bool PrintEmptyMembersOnSameLine = false;
399+
394400
/// Whether to print the extensions from conforming protocols.
395401
bool PrintExtensionFromConformingProtocols = false;
396402

lib/AST/ASTPrinter.cpp

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2311,7 +2311,8 @@ void PrintAST::printMembers(ArrayRef<Decl *> members, bool needComma,
23112311
bool openBracket, bool closeBracket) {
23122312
if (openBracket) {
23132313
Printer << " {";
2314-
Printer.printNewline();
2314+
if (!Options.PrintEmptyMembersOnSameLine || !members.empty())
2315+
Printer.printNewline();
23152316
}
23162317
{
23172318
IndentRAII indentMore(*this);
@@ -3104,13 +3105,12 @@ static FeatureSet getUniqueFeaturesUsed(Decl *decl) {
31043105
return features;
31053106
}
31063107

3107-
static void printCompatibilityCheckIf(ASTPrinter &printer,
3108-
bool isElsif,
3108+
static void printCompatibilityCheckIf(ASTPrinter &printer, bool isElseIf,
31093109
bool includeCompilerCheck,
31103110
const BasicFeatureSet &features) {
31113111
assert(!features.empty());
31123112

3113-
printer << (isElsif ? "#elsif " : "#if ");
3113+
printer << (isElseIf ? "#elseif " : "#if ");
31143114
if (includeCompilerCheck)
31153115
printer << "compiler(>=5.3) && ";
31163116

@@ -3126,7 +3126,7 @@ static void printCompatibilityCheckIf(ASTPrinter &printer,
31263126
printer.printNewline();
31273127
}
31283128

3129-
/// Generate a #if ... #elsif ... #endif chain for the given
3129+
/// Generate a #if ... #elseif ... #endif chain for the given
31303130
/// suppressible feature checks.
31313131
static void printWithSuppressibleFeatureChecks(ASTPrinter &printer,
31323132
PrintOptions &options,
@@ -3147,18 +3147,17 @@ static void printWithSuppressibleFeatureChecks(ASTPrinter &printer,
31473147
return;
31483148
}
31493149

3150-
// Otherwise, enter a `#if` or `#elsif` for the next feature.
3150+
// Otherwise, enter a `#if` or `#elseif` for the next feature.
31513151
Feature feature = generator.next();
3152-
printCompatibilityCheckIf(printer, /*elsif*/ !firstInChain,
3153-
includeCompilerCheck,
3154-
{feature});
3152+
printCompatibilityCheckIf(printer, /*elseif*/ !firstInChain,
3153+
includeCompilerCheck, {feature});
31553154

31563155
// Print the body.
31573156
printBody();
31583157
printer.printNewline();
31593158

31603159
// Start suppressing the feature and recurse to either generate
3161-
// more `#elsif` clauses or finish off with `#endif`.
3160+
// more `#elseif` clauses or finish off with `#endif`.
31623161
suppressingFeature(options, feature, [&] {
31633162
printWithSuppressibleFeatureChecks(printer, options, /*first*/ false,
31643163
includeCompilerCheck, generator,
@@ -3171,13 +3170,13 @@ static void printWithSuppressibleFeatureChecks(ASTPrinter &printer,
31713170
///
31723171
/// In the most general form, with both required features and multiple
31733172
/// suppressible features in play, the generated code pattern looks like
3174-
/// the following (assuming that feaature $bar implies feature $baz):
3173+
/// the following (assuming that feature $bar implies feature $baz):
31753174
///
31763175
/// ```
31773176
/// #if compiler(>=5.3) && $foo
31783177
/// #if $bar
31793178
/// @foo @bar @baz func @test() {}
3180-
/// #elsif $baz
3179+
/// #elseif $baz
31813180
/// @foo @baz func @test() {}
31823181
/// #else
31833182
/// @foo func @test() {}
@@ -3205,7 +3204,7 @@ void swift::printWithCompatibilityFeatureChecks(ASTPrinter &printer,
32053204
bool hasRequiredFeatures = features.hasAnyRequired();
32063205
if (hasRequiredFeatures) {
32073206
printCompatibilityCheckIf(printer,
3208-
/*elsif*/ false,
3207+
/*elseif*/ false,
32093208
/*compiler check*/ true,
32103209
features.requiredFeatures());
32113210
}

lib/Frontend/ModuleInterfaceSupport.cpp

Lines changed: 56 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
#include "swift/AST/FileSystem.h"
2020
#include "swift/AST/Module.h"
2121
#include "swift/AST/ModuleNameLookup.h"
22+
#include "swift/AST/NameLookupRequests.h"
2223
#include "swift/AST/ProtocolConformance.h"
24+
#include "swift/AST/TypeCheckRequests.h"
2325
#include "swift/AST/TypeRepr.h"
2426
#include "swift/Basic/STLExtras.h"
2527
#include "swift/Frontend/Frontend.h"
@@ -570,13 +572,14 @@ class InheritedProtocolCollector {
570572
});
571573
}
572574

575+
// Preserve the behavior of previous implementations which formatted of
576+
// empty extensions compactly with '{}' on the same line.
577+
PrintOptions extensionPrintOptions = printOptions;
578+
extensionPrintOptions.PrintEmptyMembersOnSameLine = true;
579+
573580
// Then walk the remaining ones, and see what we need to print.
574-
// Note: We could do this in one pass, but the logic is easier to
575-
// understand if we build up the list and then print it, even if it takes
576-
// a bit more memory.
577581
// FIXME: This will pick the availability attributes from the first sight
578582
// of a protocol rather than the maximally available case.
579-
SmallVector<ProtocolAndAvailability, 16> protocolsToPrint;
580583
for (const auto &protoAndAvailability : ExtraProtocols) {
581584
auto proto = std::get<0>(protoAndAvailability);
582585
auto availability = std::get<1>(protoAndAvailability);
@@ -602,58 +605,64 @@ class InheritedProtocolCollector {
602605
if (isPublicOrUsableFromInline(inherited) &&
603606
conformanceDeclaredInModule(M, nominal, inherited) &&
604607
!M->isImportedImplementationOnly(inherited->getParentModule())) {
605-
protocolsToPrint.push_back(
606-
ProtocolAndAvailability(inherited, availability, isUnchecked,
607-
otherAttrs));
608+
auto protoAndAvailability = ProtocolAndAvailability(
609+
inherited, availability, isUnchecked, otherAttrs);
610+
printSynthesizedExtension(out, extensionPrintOptions, M, nominal,
611+
protoAndAvailability);
608612
return TypeWalker::Action::SkipChildren;
609613
}
610614

611615
return TypeWalker::Action::Continue;
612616
});
613617
}
614-
if (protocolsToPrint.empty())
615-
return;
616-
617-
for (const auto &protoAndAvailability : protocolsToPrint) {
618-
StreamPrinter printer(out);
619-
auto proto = std::get<0>(protoAndAvailability);
620-
auto availability = std::get<1>(protoAndAvailability);
621-
auto isUnchecked = std::get<2>(protoAndAvailability);
622-
auto otherAttrs = std::get<3>(protoAndAvailability);
623-
624-
PrintOptions curPrintOptions = printOptions;
625-
auto printBody = [&] {
626-
// FIXME: Shouldn't this be an implicit conversion?
627-
TinyPtrVector<const DeclAttribute *> attrs;
628-
attrs.insert(attrs.end(), availability.begin(), availability.end());
629-
auto spiAttributes = proto->getAttrs().getAttributes<SPIAccessControlAttr>();
630-
attrs.insert(attrs.end(), spiAttributes.begin(), spiAttributes.end());
631-
attrs.insert(attrs.end(), otherAttrs.begin(), otherAttrs.end());
632-
DeclAttributes::print(printer, curPrintOptions, attrs);
633-
634-
printer << "extension ";
635-
{
636-
bool oldFullyQualifiedTypesIfAmbiguous =
637-
curPrintOptions.FullyQualifiedTypesIfAmbiguous;
638-
curPrintOptions.FullyQualifiedTypesIfAmbiguous =
639-
curPrintOptions.FullyQualifiedExtendedTypesIfAmbiguous;
640-
nominal->getDeclaredType().print(printer, curPrintOptions);
641-
curPrintOptions.FullyQualifiedTypesIfAmbiguous =
642-
oldFullyQualifiedTypesIfAmbiguous;
643-
}
644-
printer << " : ";
645-
646-
if (isUnchecked)
647-
printer << "@unchecked ";
618+
}
648619

649-
proto->getDeclaredInterfaceType()->print(printer, curPrintOptions);
620+
/// Prints a dummy extension on \p nominal to \p out for a public conformance
621+
/// to the protocol contained by \p protoAndAvailability.
622+
static void
623+
printSynthesizedExtension(raw_ostream &out, const PrintOptions &printOptions,
624+
ModuleDecl *M, const NominalTypeDecl *nominal,
625+
ProtocolAndAvailability &protoAndAvailability) {
626+
StreamPrinter printer(out);
627+
628+
auto proto = std::get<0>(protoAndAvailability);
629+
auto availability = std::get<1>(protoAndAvailability);
630+
auto isUnchecked = std::get<2>(protoAndAvailability);
631+
auto otherAttrs = std::get<3>(protoAndAvailability);
632+
633+
// Create a synthesized ExtensionDecl for the conformance.
634+
ASTContext &ctx = M->getASTContext();
635+
auto inherits = ctx.AllocateCopy(llvm::makeArrayRef(InheritedEntry(
636+
TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()), isUnchecked)));
637+
auto extension =
638+
ExtensionDecl::create(ctx, SourceLoc(), nullptr, inherits,
639+
nominal->getModuleScopeContext(), nullptr);
640+
extension->setImplicit();
641+
642+
// Build up synthesized DeclAttributes for the extension.
643+
TinyPtrVector<const DeclAttribute *> attrs;
644+
attrs.insert(attrs.end(), availability.begin(), availability.end());
645+
auto spiAttributes =
646+
proto->getAttrs().getAttributes<SPIAccessControlAttr>();
647+
attrs.insert(attrs.end(), spiAttributes.begin(), spiAttributes.end());
648+
attrs.insert(attrs.end(), otherAttrs.begin(), otherAttrs.end());
649+
650+
// Since DeclAttributes is a linked list where each added attribute becomes
651+
// the head, we need to add these attributes in reverse order to reproduce
652+
// the order in which previous implementations printed these attributes.
653+
DeclAttributes declAttrs;
654+
for (auto attr = attrs.rbegin(), end = attrs.rend(); attr != end; ++attr) {
655+
declAttrs.add(const_cast<DeclAttribute *>(*attr));
656+
}
657+
extension->getAttrs() = declAttrs;
650658

651-
printer << " {}";
652-
};
659+
ctx.evaluator.cacheOutput(ExtendedTypeRequest{extension},
660+
nominal->getDeclaredType());
661+
ctx.evaluator.cacheOutput(ExtendedNominalRequest{extension},
662+
const_cast<NominalTypeDecl *>(nominal));
653663

654-
printBody();
655-
printer << "\n";
656-
}
664+
extension->print(printer, printOptions);
665+
printer << "\n";
657666
}
658667

659668
/// If there were any conditional conformances that couldn't be printed,

test/ModuleInterface/features.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// RUN: %empty-directory(%t)
22

3-
// RUN: %target-swift-frontend -typecheck -swift-version 5 -module-name FeatureTest -emit-module-interface-path - -enable-library-evolution -disable-availability-checking %s | %FileCheck %s
4-
// REQUIRES: concurrency
3+
// RUN: %target-swift-frontend -typecheck -swift-version 5 -module-name FeatureTest -emit-module-interface-path %t/FeatureTest.swiftinterface -enable-library-evolution -disable-availability-checking %s
4+
// RUN: %FileCheck %s < %t/FeatureTest.swiftinterface
5+
// RUN: %target-swift-frontend -typecheck-module-from-interface -disable-availability-checking -swift-version 5 -module-name FeatureTest %t/FeatureTest.swiftinterface
56

67
// REQUIRES: concurrency
78

@@ -166,7 +167,7 @@ public func unsafeInheritExecutor() async {}
166167
// CHECK-NEXT: #if $UnsafeInheritExecutor
167168
// CHECK-NEXT: @_specialize{{.*}}
168169
// CHECK-NEXT: @_unsafeInheritExecutor public func multipleSuppressible<T>(value: T) async
169-
// CHECK-NEXT: #elsif $SpecializeAttributeWithAvailability
170+
// CHECK-NEXT: #elseif $SpecializeAttributeWithAvailability
170171
// CHECK-NEXT: @_specialize{{.*}}
171172
// CHECK-NEXT: public func multipleSuppressible<T>(value: T) async
172173
// CHECK-NEXT: #else
@@ -195,3 +196,7 @@ public func unavailableFromAsyncFunc() { }
195196
public func noAsyncFunc() { }
196197

197198
// CHECK-NOT: extension FeatureTest.MyActor : Swift.Sendable
199+
200+
// CHECK: #if compiler(>=5.3) && $GlobalActors
201+
// CHECK-NEXT: extension FeatureTest.SomeGlobalActor : _Concurrency.GlobalActor {}
202+
// CHECK-NEXT: #endif

0 commit comments

Comments
 (0)