Skip to content

Commit 38c23bc

Browse files
committed
[Serialization] Allow near-misses when matching imported Clang declarations
Fixes the rest of rdar://137014448.
1 parent efbd1af commit 38c23bc

File tree

8 files changed

+91
-4
lines changed

8 files changed

+91
-4
lines changed

lib/Serialization/Deserialization.cpp

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2000,6 +2000,33 @@ static bool isReExportedToModule(const ValueDecl *value,
20002000
return exportedName == expectedModule->getName().str();
20012001
}
20022002

2003+
namespace {
2004+
/// The result of a type comparison.
2005+
enum class TypeComparison {
2006+
NotEqual,
2007+
Equal,
2008+
NearMatch,
2009+
};
2010+
2011+
TypeComparison compareTypes(CanType type1, CanType type2, bool nearMatchOk) {
2012+
if (type1->isEqual(type2))
2013+
return TypeComparison::Equal;
2014+
2015+
if (nearMatchOk) {
2016+
TypeMatchOptions options = TypeMatchFlags::AllowTopLevelOptionalMismatch;
2017+
options |= TypeMatchFlags::AllowNonOptionalForIUOParam;
2018+
options |= TypeMatchFlags::IgnoreNonEscapingForOptionalFunctionParam;
2019+
options |= TypeMatchFlags::IgnoreFunctionSendability;
2020+
options |= TypeMatchFlags::IgnoreSendability;
2021+
options |= TypeMatchFlags::IgnoreFunctionGlobalActorIsolation;
2022+
if (type1->matches(type2, options))
2023+
return TypeComparison::NearMatch;
2024+
}
2025+
2026+
return TypeComparison::NotEqual;
2027+
}
2028+
}
2029+
20032030
/// Remove values from \p values that don't match the expected type or module.
20042031
///
20052032
/// Any of \p expectedTy, \p expectedModule, or \p expectedGenericSig can be
@@ -2015,8 +2042,10 @@ static void filterValues(Type expectedTy, ModuleDecl *expectedModule,
20152042
if (expectedTy)
20162043
canTy = expectedTy->getCanonicalType();
20172044

2045+
llvm::TinyPtrVector<ValueDecl *> clangNearMatches;
2046+
20182047
auto newEnd = std::remove_if(values.begin(), values.end(),
2019-
[=](ValueDecl *value) {
2048+
[=, &clangNearMatches](ValueDecl *value) {
20202049
// Ignore anything that was parsed (vs. deserialized), because a serialized
20212050
// module cannot refer to it.
20222051
if (value->getDeclContext()->getParentSourceFile())
@@ -2026,7 +2055,14 @@ static void filterValues(Type expectedTy, ModuleDecl *expectedModule,
20262055
return true;
20272056

20282057
// If we're expecting a type, make sure this decl has the expected type.
2029-
if (canTy && !value->getInterfaceType()->isEqual(canTy))
2058+
TypeComparison typesMatch = TypeComparison::Equal;
2059+
if (canTy) {
2060+
typesMatch = compareTypes(canTy,
2061+
value->getInterfaceType()->getCanonicalType(),
2062+
importedFromClang);
2063+
}
2064+
2065+
if (typesMatch == TypeComparison::NotEqual)
20302066
return true;
20312067

20322068
if (value->isStatic() != isStatic)
@@ -2075,9 +2111,20 @@ static void filterValues(Type expectedTy, ModuleDecl *expectedModule,
20752111
cast<ConstructorDecl>(value)->getInitKind() != *ctorInit)
20762112
return true;
20772113
}
2114+
2115+
// Record near matches.
2116+
if (typesMatch == TypeComparison::NearMatch) {
2117+
clangNearMatches.push_back(value);
2118+
return true;
2119+
}
2120+
2121+
ASSERT(typesMatch == TypeComparison::Equal);
20782122
return false;
20792123
});
20802124
values.erase(newEnd, values.end());
2125+
2126+
if (values.empty())
2127+
values.append(clangNearMatches.begin(), clangNearMatches.end());
20812128
}
20822129

20832130
/// Look for nested types in all files of \p extensionModule except from the \p thisFile.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
void takes_a_void_pointer(const void * _Nonnull pointer);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
void takes_a_void_pointer(const void * _Nullable pointer);

test/SIL/Serialization/Inputs/extern_with_nonnull.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
#if USE_EXTERN
12
@_extern(c, "takes_a_void_pointer")
23
public func takes_a_void_pointer(_ pointer: UnsafeRawPointer)
4+
#elseif USE_C_MODULE
5+
import CTakesPtrNonNull
6+
#else
7+
#error("Not configured")
8+
#endif
39

410
@_alwaysEmitIntoClient
511
public func callWithNonNull() {

test/SIL/Serialization/Inputs/extern_with_nullable.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
#if USE_EXTERN
12
@_extern(c, "takes_a_void_pointer")
23
public func takes_a_void_pointer(_ pointer: UnsafeRawPointer?)
4+
#elseif USE_C_MODULE
5+
import CTakesPtrNullable
6+
#else
7+
#error("Not configured")
8+
#endif
39

410
@_alwaysEmitIntoClient
511
public func callWithNullable() {
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module CTakesPtrNullable {
2+
header "c_takes_ptr_nullable.h"
3+
}
4+
5+
module CTakesPtrNonNull {
6+
header "c_takes_ptr_nonnull.h"
7+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -DUSE_C_MODULE -I %S/Inputs -o %t %S/Inputs/extern_with_nullable.swift -module-name c_with_nullable -enable-experimental-feature Extern
3+
// RUN: %target-swift-frontend -emit-module -DUSE_C_MODULE -I %S/Inputs -o %t %S/Inputs/extern_with_nonnull.swift -module-name c_with_nonnull -enable-experimental-feature Extern
4+
// RUN: %target-swift-frontend -emit-sil -o %t -I %t -primary-file %s -module-name main -O -enable-experimental-feature Extern
5+
6+
// RUN: %target-swift-frontend -emit-ir -o %t -I %t -primary-file %s -module-name main -O -enable-experimental-feature Extern
7+
8+
// Don't crash or otherwise fail when inlining multiple functions that reference
9+
// C declarations of the same name but different types at the SIL level.
10+
11+
// REQUIRES: swift_feature_Extern
12+
13+
import c_with_nullable
14+
import c_with_nonnull
15+
16+
public func main() {
17+
callWithNullable()
18+
callWithNonNull()
19+
}

test/SIL/Serialization/extern_collision.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %empty-directory(%t)
2-
// RUN: %target-swift-frontend -emit-module -enable-experimental-feature Extern -o %t %S/Inputs/extern_with_nullable.swift
3-
// RUN: %target-swift-frontend -emit-module -enable-experimental-feature Extern -o %t %S/Inputs/extern_with_nonnull.swift
2+
// RUN: %target-swift-frontend -emit-module -DUSE_EXTERN -enable-experimental-feature Extern -o %t %S/Inputs/extern_with_nullable.swift
3+
// RUN: %target-swift-frontend -emit-module -DUSE_EXTERN -enable-experimental-feature Extern -o %t %S/Inputs/extern_with_nonnull.swift
44
// RUN: %target-swift-frontend -emit-sil -o %t -I %t -primary-file %s -module-name main -O
55

66
// REQUIRES: swift_feature_Extern

0 commit comments

Comments
 (0)