Skip to content

Commit

Permalink
Merge pull request #74142 from xymus/access-level-conformances
Browse files Browse the repository at this point in the history
Sema: Report public conformances to non-publicly imported protocols
  • Loading branch information
xymus authored Jun 14, 2024
2 parents 4b374cd + e5bc35b commit 4dc0603
Show file tree
Hide file tree
Showing 15 changed files with 186 additions and 36 deletions.
Binary file added PrivateLib
Binary file not shown.
9 changes: 6 additions & 3 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -3648,7 +3648,8 @@ ERROR(decl_from_hidden_module,none,
"cannot use %kind0 %select{here|as property wrapper here|"
"as result builder here|"
"in an extension with public or '@usableFromInline' members|"
"in an extension with conditional conformances}1; "
"in an extension with conditional conformances|"
"in a public or '@usableFromInline' conformance}1; "
"%select{%2 has been imported as implementation-only|"
"it is an SPI imported from %2|"
"it is SPI|"
Expand All @@ -3662,7 +3663,8 @@ ERROR(typealias_desugars_to_type_from_hidden_module,none,
"as property wrapper here|"
"as result builder here|"
"in an extension with public or '@usableFromInline' members|"
"in an extension with conditional conformances}3 "
"in an extension with conditional conformance|"
"in a public or '@usableFromInline' conformance}3 "
"because %select{%4 has been imported as implementation-only|"
"it is an SPI imported from %4|"
"<<ERROR>>|"
Expand All @@ -3675,7 +3677,8 @@ ERROR(conformance_from_implementation_only_module,none,
"cannot use conformance of %0 to %1 %select{here|as property wrapper here|"
"as result builder here|"
"in an extension with public or '@usableFromInline' members|"
"in an extension with conditional conformances}2; "
"in an extension with conditional conformances|"
"<<ERROR>>}2; "
"%select{%3 has been imported as implementation-only|"
"the conformance is declared as SPI in %3|"
"the conformance is declared as SPI|"
Expand Down
21 changes: 16 additions & 5 deletions lib/Sema/ResilienceDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,11 +248,22 @@ static bool diagnoseValueDeclRefExportability(SourceLoc loc, const ValueDecl *D,
}

// Access levels from imports are reported with the others access levels.
// Except for extensions, we report them here.
if (originKind == DisallowedOriginKind::NonPublicImport &&
reason != ExportabilityReason::ExtensionWithPublicMembers &&
reason != ExportabilityReason::ExtensionWithConditionalConformances)
return false;
// Except for extensions and protocol conformances, we report them here.
if (originKind == DisallowedOriginKind::NonPublicImport) {
bool reportHere = [&] {
switch (*reason) {
case ExportabilityReason::ExtensionWithPublicMembers:
case ExportabilityReason::ExtensionWithConditionalConformances:
return true;
case ExportabilityReason::Inheritance:
return isa<ProtocolDecl>(D);
default:
return false;
}
}();
if (!reportHere)
return false;
}

if (ctx.LangOpts.EnableModuleApiImportRemarks &&
import.has_value() && where.isExported() &&
Expand Down
4 changes: 2 additions & 2 deletions lib/Sema/TypeCheckAccess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2260,7 +2260,7 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {

for (TypeLoc inherited : nominal->getInherited().getEntries()) {
checkType(inherited.getType(), inherited.getTypeRepr(), nominal,
ExportabilityReason::General, flags);
ExportabilityReason::Inheritance, flags);
}
}

Expand Down Expand Up @@ -2362,7 +2362,7 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
// must be exported.
for (TypeLoc inherited : ED->getInherited().getEntries()) {
checkType(inherited.getType(), inherited.getTypeRepr(), ED,
ExportabilityReason::General,
ExportabilityReason::Inheritance,
DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol);
}

Expand Down
3 changes: 2 additions & 1 deletion lib/Sema/TypeCheckAvailability.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ enum class ExportabilityReason : unsigned {
PropertyWrapper,
ResultBuilder,
ExtensionWithPublicMembers,
ExtensionWithConditionalConformances
ExtensionWithConditionalConformances,
Inheritance
};

/// A description of the restrictions on what declarations can be referenced
Expand Down
8 changes: 4 additions & 4 deletions test/SPI/implementation_only_spi_import_exposability.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ public protocol IOIProtocol {}
@_spi(B) public func leakSPIStruct(_ a: SPIStruct) -> SPIStruct { fatalError() } // expected-warning 2 {{cannot use struct 'SPIStruct' here; 'Lib' has been imported as implementation-only}}
@_spi(B) public func leakIOIStruct(_ a: IOIStruct) -> IOIStruct { fatalError() } // expected-warning 2 {{cannot use struct 'IOIStruct' here; 'Lib' has been imported as implementation-only}}

public struct PublicStruct : IOIProtocol, SPIProtocol { // expected-error {{cannot use protocol 'IOIProtocol' here; 'Lib' has been imported as implementation-only}}
// expected-error @-1 {{cannot use protocol 'SPIProtocol' here; 'Lib' has been imported as implementation-only}}
public struct PublicStruct : IOIProtocol, SPIProtocol { // expected-error {{cannot use protocol 'IOIProtocol' in a public or '@usableFromInline' conformance; 'Lib' has been imported as implementation-only}}
// expected-error @-1 {{cannot use protocol 'SPIProtocol' in a public or '@usableFromInline' conformance; 'Lib' has been imported as implementation-only}}
public var spiStruct = SPIStruct() // expected-error {{cannot use struct 'SPIStruct' here; 'Lib' has been imported as implementation-only}}
public var ioiStruct = IOIStruct() // expected-error {{cannot use struct 'IOIStruct' here; 'Lib' has been imported as implementation-only}}

Expand All @@ -49,8 +49,8 @@ public struct PublicStruct : IOIProtocol, SPIProtocol { // expected-error {{cann
}

@_spi(B)
public struct LocalSPIStruct : IOIProtocol, SPIProtocol { // expected-warning {{cannot use protocol 'IOIProtocol' here; 'Lib' has been imported as implementation-only}}
// expected-warning @-1 {{cannot use protocol 'SPIProtocol' here; 'Lib' has been imported as implementation-only}}
public struct LocalSPIStruct : IOIProtocol, SPIProtocol { // expected-warning {{cannot use protocol 'IOIProtocol' in a public or '@usableFromInline' conformance; 'Lib' has been imported as implementation-only}}
// expected-warning @-1 {{cannot use protocol 'SPIProtocol' in a public or '@usableFromInline' conformance; 'Lib' has been imported as implementation-only}}
}

#endif
2 changes: 1 addition & 1 deletion test/SPI/local_spi_decls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ private protocol PrivateProtocol {} // expected-note {{type declared here}}

@_spi(S) public class BadSubclass : InternalClass {} // expected-error{{class cannot be declared public because its superclass is internal}}
@_spi(S) public class OkSPISubclass : SPIClass {} // OK
public class BadPublicClass : SPIClass {} // expected-error {{cannot use class 'SPIClass' here; it is SPI}}
public class BadPublicClass : SPIClass {} // expected-error {{cannot use class 'SPIClass' in a public or '@usableFromInline' conformance; it is SPI}}
@_spi(S) public class BadSPIClass : PrivateClass {} // expected-error {{class cannot be declared public because its superclass is private}}

@_spi(s) public func genFunc<T: PrivateProtocol>(_ t: T) {} // expected-error {{global function cannot be declared public because its generic parameter uses a private type}}
Expand Down
70 changes: 70 additions & 0 deletions test/Sema/access-level-import-conforming-types.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// RUN: %empty-directory(%t)

// RUN: %target-swift-frontend -emit-module -o %t/NormalLibrary.swiftmodule \
// RUN: %S/Inputs/implementation-only-import-in-decls-public-helper.swift \
// RUN: -enable-library-evolution -swift-version 5

// RUN: %target-swift-frontend -emit-module -o %t/BADLibrary.swiftmodule \
// RUN: %S/Inputs/implementation-only-import-in-decls-helper.swift -I %t \
// RUN: -enable-library-evolution -swift-version 5

// RUN: %target-typecheck-verify-swift -I %t \
// RUN: -swift-version 5 -package-name pkg -enable-library-evolution
// RUN: %target-typecheck-verify-swift -I %t \
// RUN: -swift-version 5 -package-name pkg

internal import BADLibrary // expected-note 9 {{protocol 'BadProto' imported as 'internal' from 'BADLibrary' here}}
// expected-note @-1 2 {{struct 'IntLike' imported as 'internal' from 'BADLibrary' here}}
// expected-note @-2 2 {{class 'BadClass' imported as 'internal' from 'BADLibrary' here}}

public protocol LocalProto {}

public struct TestConformance: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
public struct TestConformanceComposition: LocalProto & BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}

@usableFromInline struct TestConformanceUFI: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}

public class TestConformanceClass: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
public enum TestConformanceEnum: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}

public struct TestExtensionStruct {}
extension TestExtensionStruct: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}

package struct TestConformancePackage: BadProto {} // FIXME-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
package struct TestConformanceCompositionPackage: LocalProto & BadProto {} // FIXME-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}

@usableFromInline struct TestConformanceUFIPackage: BadProto {} // expected-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}

package class TestConformanceClassPackage: BadProto {} // FIXME-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}
package enum TestConformanceEnumPackage: BADLibrary.BadProto {} // FIXME-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}

package struct TestExtensionStructPackage {}
extension TestExtensionStructPackage: BadProto {} // FIXME-error {{cannot use protocol 'BadProto' in a public or '@usableFromInline' conformance; 'BADLibrary' was not imported publicly}}

/// Other inheritance types are covered by the classic access-level check.

public class TestSubclass: BadClass { // expected-error {{class cannot be declared public because its superclass is internal}}
// expected-note @-1 {{class 'BadClass' is imported by this file as 'internal' from 'BADLibrary'}}
}

public enum TestRawType: IntLike { // expected-error {{enum cannot be declared public because its raw type uses an internal type}}
// expected-note @-1 {{struct 'IntLike' is imported by this file as 'internal' from 'BADLibrary'}}
case x = 1
}

public protocol TestRefinedProto: BadProto { // expected-error {{public protocol cannot refine an internal protocol}}
// expected-note @-1 {{protocol 'BadProto' is imported by this file as 'internal' from 'BADLibrary'}}
}

package class TestSubclassPackage: BadClass { // expected-error {{class cannot be declared package because its superclass is internal}}
// expected-note @-1 {{class 'BadClass' is imported by this file as 'internal' from 'BADLibrary'}}
}

package enum TestRawTypePackage: IntLike { // expected-error {{enum cannot be declared package because its raw type uses an internal type}}
// expected-note @-1 {{struct 'IntLike' is imported by this file as 'internal' from 'BADLibrary'}}
case x = 1
}

package protocol TestRefinedProtoPackage: BadProto { // expected-error {{package protocol cannot refine an internal protocol}}
// expected-note @-1 {{protocol 'BadProto' is imported by this file as 'internal' from 'BADLibrary'}}
}
3 changes: 1 addition & 2 deletions test/Sema/access-level-import-typealias.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,10 @@ public typealias ClazzAlias = Clazz
public import Aliases
internal import Original // expected-note 2 {{class 'Clazz' imported as 'internal' from 'Original' here}}

// expected-error@+1 {{'ClazzAlias' aliases 'Original.Clazz' and cannot be used here because 'Original' was not imported publicly}}
// expected-error@+1 {{'ClazzAlias' aliases 'Original.Clazz' and cannot be used in a public or '@usableFromInline' conformance because 'Original' was not imported publicly}}
public class InheritsFromClazzAlias: ClazzAlias {}

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

15 changes: 7 additions & 8 deletions test/Sema/implementation-only-import-in-decls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,14 @@ import NormalLibrary
@_implementationOnly import BADLibrary
// expected-warning @-1 {{'@_implementationOnly' is deprecated, use 'internal import' instead}}

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

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

struct TestConformanceOkay: BadProto {} // ok

public class TestConformanceClass: BadProto {} // expected-error {{cannot use protocol 'BadProto' here; 'BADLibrary' has been imported as implementation-only}}
public enum TestConformanceEnum: BadProto {} // expected-error {{cannot use protocol 'BadProto' here; 'BADLibrary' has been imported as implementation-only}}

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

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

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

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

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

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

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

Expand Down
2 changes: 1 addition & 1 deletion test/Sema/missing-import-typealias-swift6.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ public typealias ClazzAlias = Clazz

public import Aliases

// expected-error@+1 {{'ClazzAlias' aliases 'Original.Clazz' and cannot be used here because 'Original' was not imported by this file}}
// expected-error@+1 {{'ClazzAlias' aliases 'Original.Clazz' and cannot be used in a public or '@usableFromInline' conformance because 'Original' was not imported by this file}}
public class InheritsFromClazzAlias: ClazzAlias {}

2 changes: 1 addition & 1 deletion test/Sema/missing-import-typealias.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ import Aliases

// CHECK-NON-RESILIENT-NOT: was not imported by this file

// expected-warning@+2 {{'ClazzAlias' aliases 'Original.Clazz' and cannot be used here because 'Original' was not imported by this file; this is an error in the Swift 6 language mode}}
// expected-warning@+2 {{'ClazzAlias' aliases 'Original.Clazz' and cannot be used in a public or '@usableFromInline' conformance because 'Original' was not imported by this file; this is an error in the Swift 6 language mode}}
// expected-note@+1 {{The missing import of module 'Original' will be added implicitly}}
public class InheritsFromClazzAlias: ClazzAlias {}

Expand Down
Loading

0 comments on commit 4dc0603

Please sign in to comment.