Skip to content

[5.1] [ModuleInterface] Qualify all types in module interfaces #25488

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/swift/AST/PrintOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ struct PrintOptions {
/// consistent and well-formed.
///
/// \see swift::emitParseableInterface
static PrintOptions printParseableInterfaceFile();
static PrintOptions printParseableInterfaceFile(bool preferTypeRepr);

static PrintOptions printModuleInterface();
static PrintOptions printTypeInterface(Type T);
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Frontend/ParseableInterfaceSupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ class ModuleDecl;

/// Options for controlling the generation of the .swiftinterface output.
struct ParseableInterfaceOptions {
/// Should we prefer printing TypeReprs when writing out types in a module
/// interface, or should we fully-qualify them?
bool PreserveTypesAsWritten = false;

/// Copy of all the command-line flags passed at .swiftinterface
/// generation time, re-applied to CompilerInvocation when reading
/// back .swiftinterface and reconstructing .swiftmodule.
Expand Down
5 changes: 5 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,11 @@ def build_module_from_parseable_interface :
Alias<compile_module_from_interface>,
ModeOpt;

def module_interface_preserve_types_as_written :
Flag<["-"], "module-interface-preserve-types-as-written">,
HelpText<"When emitting a module interface, preserve types as they were "
"written in the source">;

def prebuilt_module_cache_path :
Separate<["-"], "prebuilt-module-cache-path">,
HelpText<"Directory of prebuilt modules for loading module interfaces">;
Expand Down
40 changes: 38 additions & 2 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ static bool contributesToParentTypeStorage(const AbstractStorageDecl *ASD) {
return !ND->isResilient() && ASD->hasStorage() && !ASD->isStatic();
}

PrintOptions PrintOptions::printParseableInterfaceFile() {
PrintOptions PrintOptions::printParseableInterfaceFile(bool preferTypeRepr) {
PrintOptions result;
result.PrintLongAttrsOnSeparateLines = true;
result.TypeDefinitions = true;
Expand All @@ -110,6 +110,7 @@ PrintOptions PrintOptions::printParseableInterfaceFile() {
result.EnumRawValues = EnumRawValueMode::PrintObjCOnly;
result.OpaqueReturnTypePrinting =
OpaqueReturnTypePrintingMode::StableReference;
result.PreferTypeRepr = preferTypeRepr;

// We should print __consuming, __owned, etc for the module interface file.
result.SkipUnderscoredKeywords = false;
Expand Down Expand Up @@ -990,7 +991,18 @@ void PrintAST::printAttributes(const Decl *D) {
void PrintAST::printTypedPattern(const TypedPattern *TP) {
printPattern(TP->getSubPattern());
Printer << ": ";
printTypeLoc(TP->getTypeLoc());

// Make sure to check if the underlying var decl is an implicitly unwrapped
// optional.
bool isIUO = false;
if (auto *named = dyn_cast<NamedPattern>(TP->getSubPattern()))
if (auto decl = named->getDecl())
isIUO = decl->getAttrs().hasAttribute<ImplicitlyUnwrappedOptionalAttr>();

if (isIUO)
printTypeLocForImplicitlyUnwrappedOptional(TP->getTypeLoc());
else
printTypeLoc(TP->getTypeLoc());
}

/// Determines if we are required to print the name of a property declaration,
Expand Down Expand Up @@ -2538,6 +2550,14 @@ void PrintAST::visitVarDecl(VarDecl *decl) {
tyLoc = TypeLoc::withoutLoc(decl->getInterfaceType());

Printer.printDeclResultTypePre(decl, tyLoc);

// HACK: When printing result types for vars with opaque result types,
// always print them using the `some` keyword instead of printing
// the full stable reference.
llvm::SaveAndRestore<PrintOptions::OpaqueReturnTypePrintingMode>
x(Options.OpaqueReturnTypePrinting,
PrintOptions::OpaqueReturnTypePrintingMode::WithOpaqueKeyword);

if (decl->getAttrs().hasAttribute<ImplicitlyUnwrappedOptionalAttr>())
printTypeLocForImplicitlyUnwrappedOptional(tyLoc);
else
Expand Down Expand Up @@ -2813,6 +2833,14 @@ void PrintAST::visitFuncDecl(FuncDecl *decl) {

Printer.printDeclResultTypePre(decl, ResultTyLoc);
Printer.callPrintStructurePre(PrintStructureKind::FunctionReturnType);

// HACK: When printing result types for funcs with opaque result types,
// always print them using the `some` keyword instead of printing
// the full stable reference.
llvm::SaveAndRestore<PrintOptions::OpaqueReturnTypePrintingMode>
x(Options.OpaqueReturnTypePrinting,
PrintOptions::OpaqueReturnTypePrintingMode::WithOpaqueKeyword);

if (decl->getAttrs().hasAttribute<ImplicitlyUnwrappedOptionalAttr>())
printTypeLocForImplicitlyUnwrappedOptional(ResultTyLoc);
else
Expand Down Expand Up @@ -2955,6 +2983,14 @@ void PrintAST::visitSubscriptDecl(SubscriptDecl *decl) {
Printer.callPrintStructurePre(PrintStructureKind::FunctionReturnType);
if (!elementTy.getTypeRepr())
elementTy = TypeLoc::withoutLoc(decl->getElementInterfaceType());

// HACK: When printing result types for subscripts with opaque result types,
// always print them using the `some` keyword instead of printing
// the full stable reference.
llvm::SaveAndRestore<PrintOptions::OpaqueReturnTypePrintingMode>
x(Options.OpaqueReturnTypePrinting,
PrintOptions::OpaqueReturnTypePrintingMode::WithOpaqueKeyword);

if (decl->getAttrs().hasAttribute<ImplicitlyUnwrappedOptionalAttr>())
printTypeLocForImplicitlyUnwrappedOptional(elementTy);
else
Expand Down
10 changes: 1 addition & 9 deletions lib/AST/GenericSignatureBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2046,16 +2046,8 @@ TypeDecl *EquivalenceClass::lookupNestedType(
continue;
}

// If this is another type declaration, determine whether we should
// record it.
// If this is another type declaration, record it.
if (auto type = dyn_cast<TypeDecl>(member)) {
// FIXME: Filter out type declarations that aren't in the same
// module as the protocol itself. This is an unprincipled hack, but
// provides consistent lookup semantics for the generic signature
// builder in all contents.
if (type->getDeclContext()->getParentModule()
!= proto->getParentModule())
continue;

// Resolve the signature of this type.
if (!type->hasInterfaceType()) {
Expand Down
9 changes: 9 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,14 @@ static void PrintArg(raw_ostream &OS, const char *Arg, StringRef TempDir) {
OS << '"';
}

static void ParseParseableInterfaceArgs(ParseableInterfaceOptions &Opts,
ArgList &Args) {
using namespace options;

Opts.PreserveTypesAsWritten |=
Args.hasArg(OPT_module_interface_preserve_types_as_written);
}

/// Save a copy of any flags marked as ModuleInterfaceOption, if running
/// in a mode that is going to emit a .swiftinterface file.
static void SaveParseableInterfaceArgs(ParseableInterfaceOptions &Opts,
Expand Down Expand Up @@ -1300,6 +1308,7 @@ bool CompilerInvocation::parseArgs(
return true;
}

ParseParseableInterfaceArgs(ParseableInterfaceOpts, ParsedArgs);
SaveParseableInterfaceArgs(ParseableInterfaceOpts, FrontendOpts,
ParsedArgs, Diags);

Expand Down
3 changes: 2 additions & 1 deletion lib/Frontend/ParseableInterfaceSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,8 @@ bool swift::emitParseableInterface(raw_ostream &out,
printToolVersionAndFlagsComment(out, Opts, M);
printImports(out, M);

const PrintOptions printOptions = PrintOptions::printParseableInterfaceFile();
const PrintOptions printOptions = PrintOptions::printParseableInterfaceFile(
Opts.PreserveTypesAsWritten);
InheritedProtocolCollector::PerTypeMap inheritedProtocolMap;

SmallVector<Decl *, 16> topLevelDecls;
Expand Down
9 changes: 8 additions & 1 deletion stdlib/public/Darwin/XCTest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@ add_swift_target_library(swiftXCTest ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS
XCTestCaseAdditions.mm
XCTest.swift

SWIFT_COMPILE_FLAGS "${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS}"
SWIFT_COMPILE_FLAGS
"${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS}"

# XCTest has a type called XCTest, which exposes an issue in module
# interfaces -- because types are fully-qualified, the compiler currently
# doesn't disambiguate between XCTest-the-module and XCTest-the-class.
# rdar://48445154
-Xfrontend -module-interface-preserve-types-as-written
LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}"
TARGET_SDKS OSX IOS IOS_SIMULATOR TVOS TVOS_SIMULATOR
SWIFT_MODULE_DEPENDS ObjectiveC Foundation
Expand Down
1 change: 1 addition & 0 deletions test/Generics/Inputs/external-protocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
public protocol AnExternalProtocol {}
12 changes: 12 additions & 0 deletions test/Generics/typealias-in-extension-of-external-protocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// RUN: %empty-directory(%t)

// RUN: %target-swift-frontend -emit-module-path %t/ExternalProtocol.swiftmodule %S/Inputs/external-protocol.swift -module-name ExternalProtocol
// RUN: %target-swift-frontend -typecheck -I %t %s

import ExternalProtocol

extension AnExternalProtocol {
typealias TypeAlias = Int

func methodUsingAlias(_ alias: Self.TypeAlias) {}
}
9 changes: 4 additions & 5 deletions test/ParseableInterface/Inputs/enums-layout-helper.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// CHECK-LABEL: public enum FutureproofEnum : Int
// CHECK-LABEL: public enum FutureproofEnum : Swift.Int
public enum FutureproofEnum: Int {
// CHECK-NEXT: case a{{$}}
case a = 1
Expand All @@ -8,7 +8,7 @@ public enum FutureproofEnum: Int {
case c = 100
}

// CHECK-LABEL: public enum FrozenEnum : Int
// CHECK-LABEL: public enum FrozenEnum : Swift.Int
@_frozen public enum FrozenEnum: Int {
// CHECK-NEXT: case a{{$}}
case a = 1
Expand All @@ -18,7 +18,7 @@ public enum FutureproofEnum: Int {
case c = 100
}

// CHECK-LABEL: public enum FutureproofObjCEnum : Int
// CHECK-LABEL: public enum FutureproofObjCEnum : Swift.Int
@objc public enum FutureproofObjCEnum: Int {
// CHECK-NEXT: case a = 1{{$}}
case a = 1
Expand All @@ -28,7 +28,7 @@ public enum FutureproofEnum: Int {
case c = 100
}

// CHECK-LABEL: public enum FrozenObjCEnum : Int
// CHECK-LABEL: public enum FrozenObjCEnum : Swift.Int
@_frozen @objc public enum FrozenObjCEnum: Int {
// CHECK-NEXT: case a = 1{{$}}
case a = 1
Expand All @@ -37,4 +37,3 @@ public enum FutureproofEnum: Int {
// CHECK-NEXT: case c = 100{{$}}
case c = 100
}

Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -emit-module -emit-parseable-module-interface-path %t/Lib.swiftinterface -emit-module-doc -parse-stdlib -o %t/Lib.swiftmodule %s
// RUN: %target-swift-ide-test -print-module -module-to-print=Lib -access-filter-public -I %t -source-filename=x > %t/from-module.txt
// RUN: %target-swift-ide-test -print-module -module-to-print=Lib -access-filter-public -I %t -source-filename=x -prefer-type-repr=false -fully-qualified-types=true > %t/from-module.txt
// RUN: %FileCheck %s < %t/from-module.txt

// RUN: rm %t/Lib.swiftmodule
// RUN: env SWIFT_FORCE_MODULE_LOADING=prefer-serialized %target-swift-ide-test -print-module -module-to-print=Lib -access-filter-public -I %t -source-filename=x > %t/from-interface.txt
// RUN: env SWIFT_FORCE_MODULE_LOADING=prefer-serialized %target-swift-ide-test -print-module -module-to-print=Lib -access-filter-public -I %t -source-filename=x -prefer-type-repr=false -fully-qualified-types=true > %t/from-interface.txt
// RUN: diff %t/from-module.txt %t/from-interface.txt

// Try again with architecture-specific subdirectories.
// RUN: %empty-directory(%t)
// RUN: %empty-directory(%t/Lib.swiftmodule)
// RUN: %target-swift-frontend -emit-module -emit-parseable-module-interface-path %t/Lib.swiftmodule/%target-cpu.swiftinterface -emit-module-doc -parse-stdlib -o %t/Lib.swiftmodule/%target-swiftmodule-name -module-name Lib %s
// RUN: %target-swift-ide-test -print-module -module-to-print=Lib -access-filter-public -I %t -source-filename=x > %t/from-module.txt
// RUN: %target-swift-ide-test -print-module -module-to-print=Lib -access-filter-public -I %t -source-filename=x -prefer-type-repr=false -fully-qualified-types=true > %t/from-module.txt
// RUN: %FileCheck %s < %t/from-module.txt

// RUN: rm %t/Lib.swiftmodule/%target-swiftmodule-name
// RUN: env SWIFT_FORCE_MODULE_LOADING=prefer-serialized %target-swift-ide-test -print-module -module-to-print=Lib -access-filter-public -I %t -source-filename=x > %t/from-interface.txt
// RUN: env SWIFT_FORCE_MODULE_LOADING=prefer-serialized %target-swift-ide-test -print-module -module-to-print=Lib -access-filter-public -I %t -source-filename=x -prefer-type-repr=false -fully-qualified-types=true > %t/from-interface.txt
// RUN: diff %t/from-module.txt %t/from-interface.txt

/// Very important documentation!
Expand Down
40 changes: 20 additions & 20 deletions test/ParseableInterface/access-filter.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-swift-frontend -typecheck -emit-parseable-module-interface-path %t.swiftinterface %s
// RUN: %target-swift-frontend -typecheck -emit-parseable-module-interface-path %t.swiftinterface %s -module-name AccessFilter
// RUN: %FileCheck %s < %t.swiftinterface
// RUN: %FileCheck -check-prefix NEGATIVE %s < %t.swiftinterface

Expand Down Expand Up @@ -109,7 +109,7 @@ extension UFIProto {

// CHECK: extension PublicStruct {{[{]$}}
extension PublicStruct {
// CHECK: @_hasInitialValue public static var secretlySettable: Int {
// CHECK: @_hasInitialValue public static var secretlySettable: Swift.Int {
// CHECK-NEXT: get
// CHECK-NEXT: }
public private(set) static var secretlySettable: Int = 0
Expand All @@ -120,7 +120,7 @@ extension InternalStruct_BAD: PublicProto {
internal static var dummy: Int { return 0 }
}

// CHECK: extension UFIStruct : PublicProto {{[{]$}}
// CHECK: extension UFIStruct : AccessFilter.PublicProto {{[{]$}}
extension UFIStruct: PublicProto {
// CHECK-NEXT: @usableFromInline
// CHECK-NEXT: internal typealias Assoc = Swift.Int
Expand All @@ -135,7 +135,7 @@ extension UFIStruct: PublicProto {
public enum PublicEnum {
// CHECK-NEXT: case x
case x
// CHECK-NEXT: case y(Int)
// CHECK-NEXT: case y(Swift.Int)
case y(Int)
} // CHECK-NEXT: {{^[}]$}}

Expand All @@ -148,7 +148,7 @@ enum InternalEnum_BAD {
@usableFromInline enum UFIEnum {
// CHECK-NEXT: case x
case x
// CHECK-NEXT: case y(Int)
// CHECK-NEXT: case y(Swift.Int)
case y(Int)
} // CHECK-NEXT: {{^[}]$}}

Expand All @@ -167,13 +167,13 @@ class InternalClass_BAD {
// CHECK: public struct GenericStruct<T>
public struct GenericStruct<T> {}

// CHECK: extension GenericStruct where T == main.PublicStruct {{[{]$}}
extension GenericStruct where T == PublicStruct {
// CHECK: extension GenericStruct where T == AccessFilter.PublicStruct {{[{]$}}
extension GenericStruct where T == AccessFilter.PublicStruct {
// CHECK-NEXT: public func constrainedToPublicStruct(){{$}}
public func constrainedToPublicStruct() {}
} // CHECK-NEXT: {{^[}]$}}
// CHECK: extension GenericStruct where T == main.UFIStruct {{[{]$}}
extension GenericStruct where T == UFIStruct {
// CHECK: extension GenericStruct where T == AccessFilter.UFIStruct {{[{]$}}
extension GenericStruct where T == AccessFilter.UFIStruct {
// CHECK-NEXT: @usableFromInline{{$}}
// CHECK-NEXT: internal func constrainedToUFIStruct(){{$}}
@usableFromInline internal func constrainedToUFIStruct() {}
Expand All @@ -182,12 +182,12 @@ extension GenericStruct where T == InternalStruct_BAD {
@usableFromInline internal func constrainedToInternalStruct_BAD() {}
}

// CHECK: extension GenericStruct where T == main.PublicStruct {{[{]$}}
// CHECK: extension GenericStruct where T == AccessFilter.PublicStruct {{[{]$}}
extension GenericStruct where PublicStruct == T {
// CHECK-NEXT: public func constrainedToPublicStruct2(){{$}}
public func constrainedToPublicStruct2() {}
} // CHECK-NEXT: {{^[}]$}}
// CHECK: extension GenericStruct where T == main.UFIStruct {{[{]$}}
// CHECK: extension GenericStruct where T == AccessFilter.UFIStruct {{[{]$}}
extension GenericStruct where UFIStruct == T {
// CHECK-NEXT: @usableFromInline{{$}}
// CHECK-NEXT: internal func constrainedToUFIStruct2(){{$}}
Expand All @@ -197,12 +197,12 @@ extension GenericStruct where InternalStruct_BAD == T {
@usableFromInline internal func constrainedToInternalStruct2_BAD() {}
}

// CHECK: extension GenericStruct where T : main.PublicProto {{[{]$}}
// CHECK: extension GenericStruct where T : AccessFilter.PublicProto {{[{]$}}
extension GenericStruct where T: PublicProto {
// CHECK-NEXT: public func constrainedToPublicProto(){{$}}
public func constrainedToPublicProto() {}
} // CHECK-NEXT: {{^[}]$}}
// CHECK: extension GenericStruct where T : main.UFIProto {{[{]$}}
// CHECK: extension GenericStruct where T : AccessFilter.UFIProto {{[{]$}}
extension GenericStruct where T: UFIProto {
// CHECK-NEXT: @usableFromInline{{$}}
// CHECK-NEXT: internal func constrainedToUFIProto(){{$}}
Expand All @@ -212,12 +212,12 @@ extension GenericStruct where T: InternalProto_BAD {
@usableFromInline internal func constrainedToInternalProto_BAD() {}
}

// CHECK: extension GenericStruct where T : main.PublicClass {{[{]$}}
// CHECK: extension GenericStruct where T : AccessFilter.PublicClass {{[{]$}}
extension GenericStruct where T: PublicClass {
// CHECK-NEXT: public func constrainedToPublicClass(){{$}}
public func constrainedToPublicClass() {}
} // CHECK-NEXT: {{^[}]$}}
// CHECK: extension GenericStruct where T : main.UFIClass {{[{]$}}
// CHECK: extension GenericStruct where T : AccessFilter.UFIClass {{[{]$}}
extension GenericStruct where T: UFIClass {
// CHECK-NEXT: @usableFromInline{{$}}
// CHECK-NEXT: internal func constrainedToUFIClass(){{$}}
Expand All @@ -236,21 +236,21 @@ extension GenericStruct where T: AnyObject {
public struct PublicAliasBase {}
internal struct ReallyInternalAliasBase_BAD {}

// CHECK: public typealias PublicAlias = PublicAliasBase
// CHECK: public typealias PublicAlias = AccessFilter.PublicAliasBase
public typealias PublicAlias = PublicAliasBase
internal typealias InternalAlias_BAD = PublicAliasBase
// CHECK: @usableFromInline
// CHECK-NEXT: internal typealias UFIAlias = PublicAliasBase
// CHECK-NEXT: internal typealias UFIAlias = AccessFilter.PublicAliasBase
@usableFromInline internal typealias UFIAlias = PublicAliasBase

internal typealias ReallyInternalAlias_BAD = ReallyInternalAliasBase_BAD

// CHECK: extension GenericStruct where T == main.PublicAlias {{[{]$}}
// CHECK: extension GenericStruct where T == AccessFilter.PublicAlias {{[{]$}}
extension GenericStruct where T == PublicAlias {
// CHECK-NEXT: public func constrainedToPublicAlias(){{$}}
public func constrainedToPublicAlias() {}
} // CHECK-NEXT: {{^[}]$}}
// CHECK: extension GenericStruct where T == main.UFIAlias {{[{]$}}
// CHECK: extension GenericStruct where T == AccessFilter.UFIAlias {{[{]$}}
extension GenericStruct where T == UFIAlias {
// CHECK-NEXT: @usableFromInline{{$}}
// CHECK-NEXT: internal func constrainedToUFIAlias(){{$}}
Expand All @@ -275,7 +275,7 @@ extension GenericStruct: PublicProto where T: InternalProto_BAD {

public struct MultiGenericStruct<First, Second> {}

// CHECK: extension MultiGenericStruct where First == main.PublicStruct, Second == main.PublicStruct {{[{]$}}
// CHECK: extension MultiGenericStruct where First == AccessFilter.PublicStruct, Second == AccessFilter.PublicStruct {{[{]$}}
extension MultiGenericStruct where First == PublicStruct, Second == PublicStruct {
// CHECK-NEXT: public func publicPublic(){{$}}
public func publicPublic() {}
Expand Down
Loading