Skip to content

Commit 099c415

Browse files
committed
AST: Use the right module name in TypeDecl::compare()
The last step in building a generic signature is to sort the requirements. Requirements are sorted by comparing their subject types. If two requirements have the same subject type, which can only happen with conformance requirements, we break the tie by comparing protocol declarations. We compare protocol declarations using TypeDecl::compare(), which is a shortlex order on the components of the fully qualified name of a protocol (eg, Swift.Sequence, etc.) While this order is part of the ABI, it has not been updated over the years for several important changes: - It did not handle module aliases; if we import a module via an alias, we should use the real module name to compare protocols, and not the aliased name. This produced inconsistent results if the same module was imported under different names, which can happen with module interface files that use module aliases. - It did not handle the -module-abi-name flag. Changing the ABI name of a module changes how we mangle protocol names, and the order should match the mangling. This change uses the correct module names for comparison to match what the mangler does. Of course this is an ABI break, however it should have limited impact since module aliases and -module-abi-name are not widely used. Fixes rdar://147441890.
1 parent 0aeba05 commit 099c415

File tree

3 files changed

+68
-3
lines changed

3 files changed

+68
-3
lines changed

lib/AST/Decl.cpp

+34-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
//
1616
//===----------------------------------------------------------------------===//
1717

18+
#include "swift/Strings.h"
1819
#include "swift/AST/Decl.h"
1920
#include "swift/AST/ASTContext.h"
2021
#include "swift/AST/ASTMangler.h"
@@ -5365,9 +5366,39 @@ int TypeDecl::compare(const TypeDecl *type1, const TypeDecl *type2) {
53655366

53665367
// Prefer module names earlier in the alphabet.
53675368
if (dc1->isModuleScopeContext() && dc2->isModuleScopeContext()) {
5368-
auto module1 = dc1->getParentModule();
5369-
auto module2 = dc2->getParentModule();
5370-
if (int result = module1->getName().str().compare(module2->getName().str()))
5369+
// For protocol declarations specifically, the order here is part
5370+
// of the ABI, and so we must take care to get the correct module
5371+
// name for the comparison.
5372+
auto getModuleNameForOrder = [&](const TypeDecl *decl) -> StringRef {
5373+
// This used to just call getName(), which caused accidental ABI breaks
5374+
// when a module is imported under different aliases.
5375+
//
5376+
// The new behavior matches the behavior in the mangler:
5377+
//
5378+
// - getName() returns the name of the module as its imported by the
5379+
// client, which might be an alias different from the real name.
5380+
// - getRealName() returns the real name after desugaring aliases.
5381+
// - getABIName() returns the name set with the -module-abi-name flag, or
5382+
// **getName()** if the flag is not set.
5383+
//
5384+
// Thus, the correct module name to use for mangling and ordering is
5385+
// getRealName(), unless getABIName() is distinct from getName(), in
5386+
// which case we use getABIName().
5387+
//
5388+
// FIXME: This should be fixed after auditing callers of getABIName().
5389+
//
5390+
// However, to maintain ABI compatibility, we ignore the ABI name for the
5391+
// _Concurrency module, and for the compiler build of SwiftSyntax.
5392+
auto *module = decl->getDeclContext()->getParentModule();
5393+
if (module->getRealName().str() == SWIFT_CONCURRENCY_NAME ||
5394+
module->getABIName().str() == "CompilerSwiftSyntax" ||
5395+
module->getName() == module->getABIName()) {
5396+
return module->getRealName().str();
5397+
}
5398+
return module->getABIName().str();
5399+
};
5400+
5401+
if (int result = getModuleNameForOrder(type1).compare(getModuleNameForOrder(type2)))
53715402
return result;
53725403
}
53735404

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// Name: SwiftHorse, RealName: Horse, ABIName: SwiftHorse
4+
5+
// RUN: %target-swift-frontend -emit-module %s -module-name Horse -D LIB -emit-module-path %t/Horse.swiftmodule
6+
// RUN: %target-swift-emit-silgen %s -module-name main -I %t -module-alias SwiftHorse=Horse | %FileCheck %s
7+
8+
// Name: SwiftHorse, RealName: SwiftHorse, ABIName: Horse
9+
10+
// RUN: %target-swift-frontend -emit-module %s -module-name SwiftHorse -module-abi-name Horse -D LIB -emit-module-path %t/SwiftHorse.swiftmodule
11+
// RUN: %target-swift-emit-silgen %s -module-name main -I %t | %FileCheck %s
12+
13+
#if LIB
14+
public protocol Equine {}
15+
#else
16+
import SwiftHorse
17+
18+
// Make sure we use the module's real name, and not its alias name, so that
19+
// we always have Horse.Equine < Swift.Equatable. If this doesn't hold, the
20+
// two requirements in the mangling will be flipped.
21+
22+
// CHECK-LABEL: sil hidden [ossa] @$s4main21requirementOrderHorseyyx0D06EquineRzSQRzlF : $@convention(thin) <T where T : Equine, T : Equatable> (@in_guaranteed T) -> () {
23+
func requirementOrderHorse<T: Equine & Equatable>(_: T) {}
24+
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %target-swift-emit-silgen %s -module-name main | %FileCheck %s
2+
3+
// See conformance_requirement_order.swift for the general case.
4+
//
5+
// For the _Concurrency module, the mistake is baked into the ABI. For example,
6+
// we mangle the below type as Swift.Executor, but order it as if it were
7+
// _Concurrency.Executor.
8+
9+
// CHECK-LABEL: sil hidden [ossa] @$s4main27requirementOrderConcurrencyyyxSTRzScFRzlF : $@convention(thin) <T where T : Sequence, T : Executor> (@guaranteed T) -> () {
10+
func requirementOrderConcurrency<T: Executor & Sequence>(_: T) {}

0 commit comments

Comments
 (0)