Skip to content

Commit 67694c2

Browse files
committed
Sema: Report public conformances to non-publicly imported protocols
Exportability checking for non-public imports relies on classic access-level checks for some of the work. However while conforming to a local internal protocol from a public type is allow we should disallow it for imported types, even when imported as internal. Track exportability issues on conformances to protocols separately from the general category. Use that information to improve the diagnostics and report these issues for access-level on imports. rdar://128420980
1 parent b7b93a1 commit 67694c2

9 files changed

+171
-21
lines changed

PrivateLib

33.2 KB
Binary file not shown.

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3648,7 +3648,8 @@ ERROR(decl_from_hidden_module,none,
36483648
"cannot use %kind0 %select{here|as property wrapper here|"
36493649
"as result builder here|"
36503650
"in an extension with public or '@usableFromInline' members|"
3651-
"in an extension with conditional conformances}1; "
3651+
"in an extension with conditional conformances|"
3652+
"in a public or '@usableFromInline' conformance}1; "
36523653
"%select{%2 has been imported as implementation-only|"
36533654
"it is an SPI imported from %2|"
36543655
"it is SPI|"
@@ -3662,7 +3663,8 @@ ERROR(typealias_desugars_to_type_from_hidden_module,none,
36623663
"as property wrapper here|"
36633664
"as result builder here|"
36643665
"in an extension with public or '@usableFromInline' members|"
3665-
"in an extension with conditional conformances}3 "
3666+
"in an extension with conditional conformance|"
3667+
"in a public or '@usableFromInline' conformance}3 "
36663668
"because %select{%4 has been imported as implementation-only|"
36673669
"it is an SPI imported from %4|"
36683670
"<<ERROR>>|"
@@ -3675,7 +3677,8 @@ ERROR(conformance_from_implementation_only_module,none,
36753677
"cannot use conformance of %0 to %1 %select{here|as property wrapper here|"
36763678
"as result builder here|"
36773679
"in an extension with public or '@usableFromInline' members|"
3678-
"in an extension with conditional conformances}2; "
3680+
"in an extension with conditional conformances|"
3681+
"<<ERROR>>}2; "
36793682
"%select{%3 has been imported as implementation-only|"
36803683
"the conformance is declared as SPI in %3|"
36813684
"the conformance is declared as SPI|"

lib/Sema/ResilienceDiagnostics.cpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,11 +248,22 @@ static bool diagnoseValueDeclRefExportability(SourceLoc loc, const ValueDecl *D,
248248
}
249249

250250
// Access levels from imports are reported with the others access levels.
251-
// Except for extensions, we report them here.
252-
if (originKind == DisallowedOriginKind::NonPublicImport &&
253-
reason != ExportabilityReason::ExtensionWithPublicMembers &&
254-
reason != ExportabilityReason::ExtensionWithConditionalConformances)
255-
return false;
251+
// Except for extensions and protocol conformances, we report them here.
252+
if (originKind == DisallowedOriginKind::NonPublicImport) {
253+
bool reportHere = [&] {
254+
switch (*reason) {
255+
case ExportabilityReason::ExtensionWithPublicMembers:
256+
case ExportabilityReason::ExtensionWithConditionalConformances:
257+
return true;
258+
case ExportabilityReason::Inheritance:
259+
return isa<ProtocolDecl>(D);
260+
default:
261+
return false;
262+
}
263+
}();
264+
if (!reportHere)
265+
return false;
266+
}
256267

257268
if (ctx.LangOpts.EnableModuleApiImportRemarks &&
258269
import.has_value() && where.isExported() &&

lib/Sema/TypeCheckAccess.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2259,7 +2259,7 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
22592259

22602260
for (TypeLoc inherited : nominal->getInherited().getEntries()) {
22612261
checkType(inherited.getType(), inherited.getTypeRepr(), nominal,
2262-
ExportabilityReason::General, flags);
2262+
ExportabilityReason::Inheritance, flags);
22632263
}
22642264
}
22652265

@@ -2361,7 +2361,7 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
23612361
// must be exported.
23622362
for (TypeLoc inherited : ED->getInherited().getEntries()) {
23632363
checkType(inherited.getType(), inherited.getTypeRepr(), ED,
2364-
ExportabilityReason::General,
2364+
ExportabilityReason::Inheritance,
23652365
DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol);
23662366
}
23672367

lib/Sema/TypeCheckAvailability.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ enum class ExportabilityReason : unsigned {
7272
PropertyWrapper,
7373
ResultBuilder,
7474
ExtensionWithPublicMembers,
75-
ExtensionWithConditionalConformances
75+
ExtensionWithConditionalConformances,
76+
Inheritance
7677
};
7778

7879
/// A description of the restrictions on what declarations can be referenced
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend -emit-module -o %t/NormalLibrary.swiftmodule \
4+
// RUN: %S/Inputs/implementation-only-import-in-decls-public-helper.swift \
5+
// RUN: -enable-library-evolution -swift-version 5
6+
7+
// RUN: %target-swift-frontend -emit-module -o %t/BADLibrary.swiftmodule \
8+
// RUN: %S/Inputs/implementation-only-import-in-decls-helper.swift -I %t \
9+
// RUN: -enable-library-evolution -swift-version 5
10+
11+
// RUN: %target-typecheck-verify-swift -I %t \
12+
// RUN: -swift-version 5 -package-name pkg -enable-library-evolution
13+
// RUN: %target-typecheck-verify-swift -I %t \
14+
// RUN: -swift-version 5 -package-name pkg
15+
16+
internal import BADLibrary // expected-note 14 {{protocol 'BadProto' imported as 'internal' from 'BADLibrary' here}}
17+
// expected-note @-1 2 {{struct 'IntLike' imported as 'internal' from 'BADLibrary' here}}
18+
// expected-note @-2 2 {{class 'BadClass' imported as 'internal' from 'BADLibrary' here}}
19+
20+
public protocol LocalProto {}
21+
22+
public struct TestConformance: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
23+
public struct TestConformanceComposition: LocalProto & BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
24+
25+
@usableFromInline struct TestConformanceUFI: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
26+
27+
public class TestConformanceClass: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
28+
public enum TestConformanceEnum: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
29+
30+
public struct TestExtensionStruct {}
31+
extension TestExtensionStruct: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
32+
33+
package struct TestConformancePackage: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
34+
package struct TestConformanceCompositionPackage: LocalProto & BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
35+
36+
@usableFromInline struct TestConformanceUFIPackage: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
37+
38+
package class TestConformanceClassPackage: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
39+
package enum TestConformanceEnumPackage: BADLibrary.BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
40+
41+
package struct TestExtensionStructPackage {}
42+
extension TestExtensionStructPackage: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
43+
44+
/// Other inheritance types are covered by the classic access-level check.
45+
46+
public class TestSubclass: BadClass { // expected-error {{class cannot be declared public because its superclass is internal}}
47+
// expected-note @-1 {{class 'BadClass' is imported by this file as 'internal' from 'BADLibrary'}}
48+
}
49+
50+
public enum TestRawType: IntLike { // expected-error {{enum cannot be declared public because its raw type uses an internal type}}
51+
// expected-note @-1 {{struct 'IntLike' is imported by this file as 'internal' from 'BADLibrary'}}
52+
case x = 1
53+
}
54+
55+
public protocol TestRefinedProto: BadProto { // expected-error {{public protocol cannot refine an internal protocol}}
56+
// expected-note @-1 {{protocol 'BadProto' is imported by this file as 'internal' from 'BADLibrary'}}
57+
}
58+
59+
package class TestSubclassPackage: BadClass { // expected-error {{class cannot be declared package because its superclass is internal}}
60+
// expected-note @-1 {{class 'BadClass' is imported by this file as 'internal' from 'BADLibrary'}}
61+
}
62+
63+
package enum TestRawTypePackage: IntLike { // expected-error {{enum cannot be declared package because its raw type uses an internal type}}
64+
// expected-note @-1 {{struct 'IntLike' is imported by this file as 'internal' from 'BADLibrary'}}
65+
case x = 1
66+
}
67+
68+
package protocol TestRefinedProtoPackage: BadProto { // expected-error {{package protocol cannot refine an internal protocol}}
69+
// expected-note @-1 {{protocol 'BadProto' is imported by this file as 'internal' from 'BADLibrary'}}
70+
}

test/Sema/access-level-import-typealias.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,10 @@ public typealias ClazzAlias = Clazz
2424
public import Aliases
2525
internal import Original // expected-note 2 {{class 'Clazz' imported as 'internal' from 'Original' here}}
2626

27-
// expected-error@+1 {{'ClazzAlias' aliases 'Original.Clazz' and cannot be used here because 'Original' was not imported publicly}}
27+
// expected-error@+1 {{'ClazzAlias' aliases 'Original.Clazz' and cannot be used for a conformance on a public, package or '@usableFromInline' type because 'Original' was not imported publicly}}
2828
public class InheritsFromClazzAlias: ClazzAlias {}
2929

3030
@inlinable public func inlinableFunc() {
3131
// expected-error@+1 {{'ClazzAlias' aliases 'Original.Clazz' and cannot be used in an '@inlinable' function because 'Original' was not imported publicly}}
3232
_ = ClazzAlias.self
3333
}
34-

test/Sema/implementation-only-import-in-decls.swift

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,14 @@ import NormalLibrary
1111
@_implementationOnly import BADLibrary
1212
// expected-warning @-1 {{'@_implementationOnly' is deprecated, use 'internal import' instead}}
1313

14-
public struct TestConformance: BadProto {} // expected-error {{cannot use protocol 'BadProto' here; 'BADLibrary' has been imported as implementation-only}}
14+
public struct TestConformance: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' has been imported as implementation-only}}
1515

16-
@usableFromInline struct TestConformanceUFI: BadProto {} // expected-error {{cannot use protocol 'BadProto' here; 'BADLibrary' has been imported as implementation-only}}
16+
@usableFromInline struct TestConformanceUFI: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' has been imported as implementation-only}}
1717

1818
struct TestConformanceOkay: BadProto {} // ok
1919

20-
public class TestConformanceClass: BadProto {} // expected-error {{cannot use protocol 'BadProto' here; 'BADLibrary' has been imported as implementation-only}}
21-
public enum TestConformanceEnum: BadProto {} // expected-error {{cannot use protocol 'BadProto' here; 'BADLibrary' has been imported as implementation-only}}
22-
20+
public class TestConformanceClass: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' has been imported as implementation-only}}
21+
public enum TestConformanceEnum: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' has been imported as implementation-only}}
2322

2423
public struct TestGenericParams<T: BadProto> {} // expected-error {{cannot use protocol 'BadProto' here; 'BADLibrary' has been imported as implementation-only}}
2524

@@ -75,11 +74,11 @@ public protocol TestAssocTypeWhereClause {
7574
associatedtype Assoc: Collection where Assoc.Element: BadProto // expected-error {{cannot use protocol 'BadProto' here; 'BADLibrary' has been imported as implementation-only}}
7675
}
7776

78-
public enum TestRawType: IntLike { // expected-error {{cannot use struct 'IntLike' here; 'BADLibrary' has been imported as implementation-only}}
77+
public enum TestRawType: IntLike { // expected-error {{cannot use struct 'IntLike' in a public or '@usableFromInline' conformance; 'BADLibrary' has been imported as implementation-only}}
7978
case x = 1
8079
}
8180

82-
public class TestSubclass: BadClass { // expected-error {{cannot use class 'BadClass' here; 'BADLibrary' has been imported as implementation-only}}
81+
public class TestSubclass: BadClass { // expected-error {{cannot use class 'BadClass' in a public or '@usableFromInline' conformance; 'BADLibrary' has been imported as implementation-only}}
8382
}
8483

8584
public typealias TestUnderlying = BadStruct // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}}
@@ -121,7 +120,7 @@ extension Array where Element == BadStruct {
121120
subscript(okay _: Int) -> Int { 0 } // okay
122121
}
123122

124-
extension Int: @retroactive BadProto {} // expected-error {{cannot use protocol 'BadProto' here; 'BADLibrary' has been imported as implementation-only}}
123+
extension Int: @retroactive BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' has been imported as implementation-only}}
125124
struct TestExtensionConformanceOkay {}
126125
extension TestExtensionConformanceOkay: BadProto {} // okay
127126

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend -emit-module -o %t/NormalLibrary.swiftmodule \
4+
// RUN: %S/Inputs/implementation-only-import-in-decls-public-helper.swift \
5+
// RUN: -enable-library-evolution -swift-version 5
6+
7+
// RUN: %target-swift-frontend -emit-module -o %t/BADLibrary.swiftmodule \
8+
// RUN: %S/Inputs/implementation-only-import-in-decls-helper.swift -I %t \
9+
// RUN: -enable-library-evolution -swift-version 5
10+
11+
// RUN: %target-typecheck-verify-swift -I %t \
12+
// RUN: -swift-version 5 -package-name pkg -enable-library-evolution
13+
// RUN: %target-typecheck-verify-swift -I %t \
14+
// RUN: -swift-version 5 -package-name pkg
15+
16+
package import BADLibrary // expected-note 9 {{protocol 'BadProto' imported as 'package' from 'BADLibrary' here}}
17+
// expected-note @-1 {{struct 'IntLike' imported as 'package' from 'BADLibrary' here}}
18+
// expected-note @-2 {{class 'BadClass' imported as 'package' from 'BADLibrary' here}}
19+
20+
public protocol LocalProto {}
21+
22+
public struct TestConformance: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
23+
public struct TestConformanceComposition: LocalProto & BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
24+
25+
@usableFromInline struct TestConformanceUFI: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
26+
27+
public class TestConformanceClass: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
28+
public enum TestConformanceEnum: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
29+
30+
public struct TestExtensionStruct {}
31+
extension TestExtensionStruct: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
32+
33+
package struct TestConformancePackage: BadProto {}
34+
package struct TestConformanceCompositionPackage: LocalProto & BadProto {}
35+
36+
@usableFromInline struct TestConformanceUFIPackage: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
37+
38+
package class TestConformanceClassPackage: BadProto {}
39+
package enum TestConformanceEnumPackage: BADLibrary.BadProto {}
40+
41+
package struct TestExtensionStructPackage {}
42+
extension TestExtensionStructPackage: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
43+
44+
/// Other inheritance types are covered by the classic access-level check.
45+
46+
public class TestSubclass: BadClass { // expected-error {{class cannot be declared public because its superclass is package}}
47+
// expected-note @-1 {{class 'BadClass' is imported by this file as 'package' from 'BADLibrary'}}
48+
}
49+
50+
public enum TestRawType: IntLike { // expected-error {{enum cannot be declared public because its raw type uses a package type}}
51+
// expected-note @-1 {{struct 'IntLike' is imported by this file as 'package' from 'BADLibrary'}}
52+
case x = 1
53+
}
54+
55+
public protocol TestRefinedProto: BadProto { // expected-error {{public protocol cannot refine a package protocol}}
56+
// expected-note @-1 {{protocol 'BadProto' is imported by this file as 'package' from 'BADLibrary'}}
57+
}
58+
59+
package class TestSubclassPackage: BadClass {
60+
}
61+
62+
package enum TestRawTypePackage: IntLike {
63+
case x = 1
64+
}
65+
66+
package protocol TestRefinedProtoPackage: BadProto {
67+
}

0 commit comments

Comments
 (0)