diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index 0da7bf21c9cfe..6961b60e39218 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -564,6 +564,8 @@ EXPERIMENTAL_FEATURE(CheckImplementationOnlyStrict, false) /// Check that use sites have the required @_spi import for operators. EXPERIMENTAL_FEATURE(EnforceSPIOperatorGroup, true) +EXPERIMENTAL_FEATURE(StrictSemaForTextualInterface, true) + #undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE #undef EXPERIMENTAL_FEATURE #undef UPCOMING_FEATURE diff --git a/lib/AST/FeatureSet.cpp b/lib/AST/FeatureSet.cpp index cdc5f2d3472e7..7fe50617c9afd 100644 --- a/lib/AST/FeatureSet.cpp +++ b/lib/AST/FeatureSet.cpp @@ -466,6 +466,7 @@ static bool usesFeatureTildeSendable(Decl *decl) { } UNINTERESTING_FEATURE(AnyAppleOSAvailability) +UNINTERESTING_FEATURE(StrictSemaForTextualInterface) // ---------------------------------------------------------------------------- // MARK: - FeatureSet diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index ff50a3e111d41..e7ae0782c49fe 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -1762,11 +1762,14 @@ static bool checkWitnessAccess(DeclContext *dc, auto actualScopeToCheck = requiredAccessScope.first; + // without StrictTextualInterfaceChecking feature forConformance is set true + bool forConformance = not dc->getASTContext().LangOpts.hasFeature + (Feature::StrictSemaForTextualInterface); // Setting the 'forConformance' flag means that we admit witnesses in // protocol extensions that we can see, but are not necessarily as // visible as the conforming type and protocol. if (!witness->isAccessibleFrom(actualScopeToCheck.getDeclContext(), - /*forConformance=*/true)) { + forConformance)) { // Special case: if we have `@testable import` of the witness's module, // allow the witness to match if it would have matched for just this file. // That is, if '@testable' allows us to see the witness here, it should @@ -4453,7 +4456,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) { diagKind, getProtocolRequirementKind(requirement), witness, isSetter, requiredAccess, protoAccessScope.accessLevelForDiagnostics(), - proto); + proto).warnUntilFutureSwiftVersionIf(DC->getASTContext().LangOpts.hasFeature(Feature::StrictSemaForTextualInterface)); auto *decl = dyn_cast(witness); if (decl && decl->isSynthesized()) diff --git a/test/Sema/diag_internal_protocol_access_check.swift b/test/Sema/diag_internal_protocol_access_check.swift new file mode 100644 index 0000000000000..abfeb8773840c --- /dev/null +++ b/test/Sema/diag_internal_protocol_access_check.swift @@ -0,0 +1,31 @@ +// RUN: %empty-directory(%t) +// RUN: %target-typecheck-verify-swift -enable-library-evolution -enable-experimental-feature StrictSemaForTextualInterface + +public protocol P { + func foo() -> Int +} +internal protocol Q: P { + func fooImpl() -> Int32 +} +extension Q { + public func foo() -> Int { // expected-note {{mark the instance method as 'public' to satisfy the requirement}} + return Int(self.fooImpl()) + } +} +public struct S: Q { // expected-error {{method 'foo()' must be declared public because it matches a requirement in public protocol 'P'}} + internal func fooImpl() -> Int32 { + return 42 + } +} + +public struct Foo { + public init(value: Int) {} +} +public protocol PublicProtocol { + init?(integer: Int) +} +protocol InternalProtocol: PublicProtocol {} +extension InternalProtocol { + public init(integer: Int) {} // expected-note {{mark the initializer as 'public' to satisfy the requirement}} +} +extension Foo: PublicProtocol, InternalProtocol {} // expected-error {{initializer 'init(integer:)' must be declared public because it matches a requirement in public protocol 'PublicProtocol'}}