Skip to content

Commit 0e4575e

Browse files
committed
[cxx-interop] Do not consider extensions blessed by SWIFT_PRIVATE_FILEID to be retroactive
Swift warns users when they conform an imported type to an imported protocol, because such retroactive conformances are prone to producing conflicting conformances in multiple modules. When a user annotates a class as SWIFT_PRIVATE_FILEID, they are explicitly conveying that extensions in the blessed file are part of the class's private implementation (and thus privy to elevated access), so we should also not treat conformances in that file as retroactive. This patch fixes that and adds a test case. rdar://148083096
1 parent dbbdfce commit 0e4575e

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

lib/AST/ProtocolConformance.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@
2828
#include "swift/AST/Module.h"
2929
#include "swift/AST/NameLookup.h"
3030
#include "swift/AST/PackConformance.h"
31+
#include "swift/AST/Type.h"
3132
#include "swift/AST/TypeCheckRequests.h"
3233
#include "swift/AST/Types.h"
3334
#include "swift/Basic/Assertions.h"
3435
#include "swift/Basic/Statistic.h"
36+
#include "swift/ClangImporter/ClangImporter.h"
3537
#include "llvm/ADT/Statistic.h"
3638
#include "llvm/Support/PrettyStackTrace.h"
3739
#include "llvm/Support/SaveAndRestore.h"
@@ -248,6 +250,18 @@ bool ProtocolConformance::isRetroactive() const {
248250
if (isSameRetroactiveContext(extensionModule, conformingTypeModule)) {
249251
return false;
250252
}
253+
254+
auto *useSF = getDeclContext()->getOutermostParentSourceFile();
255+
auto blessesSF = [&](auto &blessed) {
256+
auto blessedFileID = SourceFile::FileIDStr::parse(blessed.first);
257+
return blessedFileID && blessedFileID->matches(useSF);
258+
};
259+
auto *clangDecl = dyn_cast_or_null<clang::CXXRecordDecl>(
260+
conformingTypeDecl->getClangDecl());
261+
if (clangDecl &&
262+
llvm::any_of(importer::getPrivateFileIDAttrs(clangDecl), blessesSF)) {
263+
return false;
264+
}
251265
}
252266

253267
return true;
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// RUN: split-file %s %t
2+
// RUN: %target-swift-frontend -typecheck -verify -I %t/Cxx/include %t/PrivateFile.swift -cxx-interoperability-mode=default -module-name Module
3+
// RUN: %target-swift-frontend -typecheck -verify -I %t/Cxx/include %t/HasRetroactive.swift -cxx-interoperability-mode=default -module-name Module
4+
// RUN: %target-swift-frontend -typecheck -verify -I %t/Cxx/include %t/NoRetroactive.swift -cxx-interoperability-mode=default -module-name Module
5+
6+
//--- Cxx/include/module.modulemap
7+
module CxxModule {
8+
requires cplusplus
9+
header "cxx-header.h"
10+
}
11+
12+
//--- Cxx/include/cxx-header.h
13+
#pragma once
14+
15+
#define SWIFT_PRIVATE_FILEID(_fileID) \
16+
__attribute__((swift_attr("private_fileid:" _fileID)))
17+
18+
class SWIFT_PRIVATE_FILEID("Module/PrivateFile.swift") Foo { void privateMethod(void) const {} };
19+
20+
//--- PrivateFile.swift
21+
import CxxModule
22+
23+
extension Foo : ExpressibleByIntegerLiteral {
24+
public typealias IntegerLiteralType = Int
25+
public init(integerLiteral: Self.IntegerLiteralType) {
26+
self.init()
27+
self.privateMethod()
28+
}
29+
}
30+
31+
//--- HasRetroactive.swift
32+
import CxxModule
33+
34+
extension Foo : @retroactive ExpressibleByIntegerLiteral {
35+
public typealias IntegerLiteralType = Int
36+
public init(integerLiteral: Self.IntegerLiteralType) {
37+
self.init()
38+
self.privateMethod() // expected-error {{'privateMethod' is inaccessible due to 'private' protection level}}
39+
}
40+
}
41+
42+
//--- NoRetroactive.swift
43+
import CxxModule
44+
45+
// expected-warning@+2 {{extension declares a conformance of imported type 'Foo' to imported protocol 'ExpressibleByIntegerLiteral'}}
46+
// expected-note@+1 {{add '@retroactive' to silence this warning}}
47+
extension Foo : ExpressibleByIntegerLiteral {
48+
public typealias IntegerLiteralType = Int
49+
public init(integerLiteral: Self.IntegerLiteralType) {
50+
self.init()
51+
self.privateMethod() // expected-error {{'privateMethod' is inaccessible due to 'private' protection level}}
52+
}
53+
}

0 commit comments

Comments
 (0)