Skip to content
Draft
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
2 changes: 2 additions & 0 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions lib/AST/FeatureSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ static bool usesFeatureTildeSendable(Decl *decl) {
}

UNINTERESTING_FEATURE(AnyAppleOSAvailability)
UNINTERESTING_FEATURE(StrictSemaForTextualInterface)

// ----------------------------------------------------------------------------
// MARK: - FeatureSet
Expand Down
7 changes: 5 additions & 2 deletions lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<AbstractFunctionDecl>(witness);
if (decl && decl->isSynthesized())
Expand Down
31 changes: 31 additions & 0 deletions test/Sema/diag_internal_protocol_access_check.swift
Original file line number Diff line number Diff line change
@@ -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
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too many blank lines

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'}}