Skip to content

Commit 66586b5

Browse files
committed
[ModuleInterface] Print alias for module names in swiftinterface files
Ambiguities are introduced in generated swiftinterfaces when a type shares a name with a module (i.e. XCTest). This workaround uses the module-alias feature to avoid these ambiguities. Writing module references with a distinguishable prefix should allow normal type-checking to avoid the usual ambiguities. We should still aim for a proper fully-qualified named syntax, but this may help in the mean time. rdar://101969500
1 parent 6d5e1b2 commit 66586b5

File tree

7 files changed

+121
-5
lines changed

7 files changed

+121
-5
lines changed

include/swift/AST/ASTPrinter.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
#include "llvm/Support/raw_ostream.h"
2525
#include "swift/AST/PrintOptions.h"
2626

27+
// Prefix to use when printing module names in module interfaces to avoid
28+
// ambiguities with type names, in AliasModuleNames mode.
29+
#define MODULE_DISAMBIGUATING_PREFIX "Module___"
30+
2731
namespace swift {
2832
class Decl;
2933
class DeclContext;

include/swift/AST/PrintOptions.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,10 @@ struct PrintOptions {
466466
/// of the alias.
467467
bool PrintTypeAliasUnderlyingType = false;
468468

469+
/// Use aliases when printing references to modules to avoid ambiguities
470+
/// with types sharing a name with a module.
471+
bool AliasModuleNames = false;
472+
469473
/// When printing an Optional<T>, rather than printing 'T?', print
470474
/// 'T!'. Used as a modifier only when we know we're printing
471475
/// something that was declared as an implicitly unwrapped optional
@@ -601,7 +605,7 @@ struct PrintOptions {
601605
return result;
602606
}
603607

604-
/// Retrieve the set of options suitable for interface generation.
608+
/// Retrieve the set of options suitable for IDE interface generation.
605609
static PrintOptions printInterface(bool printFullConvention) {
606610
PrintOptions result =
607611
printForDiagnostics(AccessLevel::Public, printFullConvention);
@@ -636,7 +640,8 @@ struct PrintOptions {
636640
static PrintOptions printSwiftInterfaceFile(ModuleDecl *ModuleToPrint,
637641
bool preferTypeRepr,
638642
bool printFullConvention,
639-
bool printSPIs);
643+
bool printSPIs,
644+
bool aliasModuleNames);
640645

641646
/// Retrieve the set of options suitable for "Generated Interfaces", which
642647
/// are a prettified representation of the public API of a module, to be

include/swift/Frontend/ModuleInterfaceSupport.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ struct ModuleInterfaceOptions {
3333
/// interface, or should we fully-qualify them?
3434
bool PreserveTypesAsWritten = false;
3535

36+
/// Use aliases when printing references to modules to avoid ambiguities
37+
/// with types sharing a name with a module.
38+
bool AliasModuleNames = false;
39+
3640
/// See \ref FrontendOptions.PrintFullConvention.
3741
/// [TODO: Clang-type-plumbing] This check should go away.
3842
bool PrintFullConvention = false;

lib/AST/ASTPrinter.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,8 @@ static bool contributesToParentTypeStorage(const AbstractStorageDecl *ASD) {
131131
PrintOptions PrintOptions::printSwiftInterfaceFile(ModuleDecl *ModuleToPrint,
132132
bool preferTypeRepr,
133133
bool printFullConvention,
134-
bool printSPIs) {
134+
bool printSPIs,
135+
bool aliasModuleNames) {
135136
PrintOptions result;
136137
result.IsForSwiftInterface = true;
137138
result.PrintLongAttrsOnSeparateLines = true;
@@ -152,6 +153,7 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(ModuleDecl *ModuleToPrint,
152153
result.OpaqueReturnTypePrinting =
153154
OpaqueReturnTypePrintingMode::StableReference;
154155
result.PreferTypeRepr = preferTypeRepr;
156+
result.AliasModuleNames = aliasModuleNames;
155157
if (printFullConvention)
156158
result.PrintFunctionRepresentationAttrs =
157159
PrintOptions::FunctionRepresentationMode::Full;
@@ -367,6 +369,9 @@ void ASTPrinter::printTypeRef(Type T, const TypeDecl *RefTo, Identifier Name,
367369

368370
void ASTPrinter::printModuleRef(ModuleEntity Mod, Identifier Name,
369371
const PrintOptions &Options) {
372+
if (Options.AliasModuleNames)
373+
printTextImpl(MODULE_DISAMBIGUATING_PREFIX);
374+
370375
printName(Name);
371376
}
372377

lib/Frontend/CompilerInvocation.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,10 @@ static void ParseModuleInterfaceArgs(ModuleInterfaceOptions &Opts,
368368

369369
Opts.PreserveTypesAsWritten |=
370370
Args.hasArg(OPT_module_interface_preserve_types_as_written);
371+
Opts.AliasModuleNames |=
372+
Args.hasFlag(OPT_alias_module_names_in_module_interface,
373+
OPT_disable_alias_module_names_in_module_interface,
374+
false);
371375
Opts.PrintFullConvention |=
372376
Args.hasArg(OPT_experimental_print_full_convention);
373377
Opts.ExperimentalSPIImports |=

lib/Frontend/ModuleInterfaceSupport.cpp

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,32 @@ static void printToolVersionAndFlagsComment(raw_ostream &out,
5858
out << "// " SWIFT_COMPILER_VERSION_KEY ": "
5959
<< ToolsVersion << "\n";
6060
out << "// " SWIFT_MODULE_FLAGS_KEY ": "
61-
<< Opts.Flags << "\n";
61+
<< Opts.Flags;
62+
63+
// Insert additional -module-alias flags
64+
if (Opts.AliasModuleNames) {
65+
llvm::SmallSet<StringRef, 2> aliasTargets;
66+
StringRef moduleName = M->getNameStr();
67+
aliasTargets.insert(M->getNameStr());
68+
out << " -module-alias " << MODULE_DISAMBIGUATING_PREFIX <<
69+
moduleName << "=" << moduleName;
70+
71+
SmallVector<ImportedModule> imports;
72+
M->getImportedModules(imports,
73+
{ModuleDecl::ImportFilterKind::Default,
74+
ModuleDecl::ImportFilterKind::Exported,
75+
ModuleDecl::ImportFilterKind::SPIOnly,
76+
ModuleDecl::ImportFilterKind::SPIAccessControl});
77+
for (ImportedModule import: imports) {
78+
StringRef importedName = import.importedModule->getNameStr();
79+
if (aliasTargets.insert(importedName).second) {
80+
out << " -module-alias " << MODULE_DISAMBIGUATING_PREFIX <<
81+
importedName << "=" << importedName;
82+
}
83+
}
84+
}
85+
out << "\n";
86+
6287
if (!Opts.IgnorableFlags.empty()) {
6388
out << "// " SWIFT_MODULE_FLAGS_IGNORABLE_KEY ": "
6489
<< Opts.IgnorableFlags << "\n";
@@ -295,6 +320,8 @@ static void printImports(raw_ostream &out,
295320
}
296321

297322
out << "import ";
323+
if (Opts.AliasModuleNames)
324+
out << MODULE_DISAMBIGUATING_PREFIX;
298325
importedModule->getReverseFullModuleName().printForward(out);
299326

300327
// Write the access path we should be honoring but aren't.
@@ -758,7 +785,8 @@ bool swift::emitSwiftInterface(raw_ostream &out,
758785
printImports(out, Opts, M);
759786

760787
const PrintOptions printOptions = PrintOptions::printSwiftInterfaceFile(
761-
M, Opts.PreserveTypesAsWritten, Opts.PrintFullConvention, Opts.PrintSPIs);
788+
M, Opts.PreserveTypesAsWritten, Opts.PrintFullConvention, Opts.PrintSPIs,
789+
Opts.AliasModuleNames);
762790
InheritedProtocolCollector::PerTypeMap inheritedProtocolMap;
763791

764792
SmallVector<Decl *, 16> topLevelDecls;
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/// Test that the AliasModuleNames mode avoids ambiguities in swiftinterfaces
2+
3+
// RUN: %empty-directory(%t)
4+
// RUN: split-file %s %t
5+
6+
// RUN: %target-swift-frontend -emit-module -module-name AmbiguousLib \
7+
// RUN: -swift-version 5 -enable-library-evolution \
8+
// RUN: -o %t/AmbiguousLib.swiftmodule \
9+
// RUN: -emit-module-interface-path %t/AmbiguousLib.swiftinterface \
10+
// RUN: %t/AmbiguousLib.swift
11+
// RUN: %target-swift-typecheck-module-from-interface(%t/AmbiguousLib.swiftinterface)
12+
13+
// RUN: %target-swift-frontend -emit-module -module-name AmbiguousClientName \
14+
// RUN: -swift-version 5 -enable-library-evolution \
15+
// RUN: -o %t/AmbiguousClientName.swiftmodule \
16+
// RUN: -emit-module-interface-path %t/AmbiguousClientName.swiftinterface \
17+
// RUN: %t/AmbiguousClientName.swift -I%t \
18+
// RUN: -alias-module-names-in-module-interface
19+
// RUN: %target-swift-typecheck-module-from-interface(%t/AmbiguousClientName.swiftinterface) -I%t
20+
21+
//--- module.modulemap
22+
module AmbiguousClientName {
23+
header "AmbiguousClientName.h"
24+
}
25+
26+
module SomeClangModule {
27+
header "SomeClangModule.h"
28+
}
29+
30+
//--- AmbiguousClientName.h
31+
struct UnderlyingType {};
32+
void underlyingFunc() {}
33+
34+
//--- SomeClangModule.h
35+
struct CType {};
36+
37+
//--- AmbiguousLib.swift
38+
39+
// 1. AmbiguousLib defined a type named AmbiguousLib
40+
public struct AmbiguousLib {
41+
public struct Nested {}
42+
}
43+
44+
// 2. A lib defines a type of the same name as a client's module
45+
public struct AmbiguousClientName {
46+
}
47+
48+
//--- AmbiguousClientName.swift
49+
50+
@_exported import AmbiguousClientName
51+
import AmbiguousLib
52+
import SomeClangModule
53+
54+
public struct SomeType {
55+
@inlinable
56+
public func inlinableFunc() {
57+
var x: AmbiguousClientName
58+
}
59+
}
60+
61+
public func refToLocalType(_ a: SomeType) {}
62+
public func refToNestedInLib(_ a: AmbiguousLib.Nested) {}
63+
64+
public func refToStdlib(_ a: Swift.Int) {}
65+
public func refToUnderlying(_ a: UnderlyingType) {}
66+
public func refToC(_ a: CType) {}

0 commit comments

Comments
 (0)