Skip to content

Sema: Report public conformances to non-publicly imported protocols #74142

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -2259,7 +2259,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 @@ -2361,7 +2361,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