[Sema] Evaluate SPI groups for all decls, not only public ones

Remove the fast path skipping evaluating SPI groups for non-public
decls. This knowledge is still required to allow the use of SPI types in
the signatures of `@usableFromInline` declarations and in internal
properties of structs in non library evolution compilation.

rdar://68530659
rdar://68527580
This commit is contained in:
Alexis Laferrière
2020-09-08 14:08:33 -07:00
parent 03adbe35da
commit cbb7228451
4 changed files with 37 additions and 16 deletions

View File

@@ -2049,10 +2049,8 @@ bool Decl::isSPI() const {
} }
ArrayRef<Identifier> Decl::getSPIGroups() const { ArrayRef<Identifier> Decl::getSPIGroups() const {
if (auto vd = dyn_cast<ValueDecl>(this)) { if (!isa<ValueDecl>(this) &&
if (vd->getFormalAccess() < AccessLevel::Public) !isa<ExtensionDecl>(this))
return ArrayRef<Identifier>();
} else if (!isa<ExtensionDecl>(this))
return ArrayRef<Identifier>(); return ArrayRef<Identifier>();
return evaluateOrDefault(getASTContext().evaluator, return evaluateOrDefault(getASTContext().evaluator,
@@ -2063,10 +2061,8 @@ ArrayRef<Identifier> Decl::getSPIGroups() const {
llvm::ArrayRef<Identifier> llvm::ArrayRef<Identifier>
SPIGroupsRequest::evaluate(Evaluator &evaluator, const Decl *decl) const { SPIGroupsRequest::evaluate(Evaluator &evaluator, const Decl *decl) const {
// Applies only to public ValueDecls and ExtensionDecls. // Applies only to public ValueDecls and ExtensionDecls.
if (auto vd = dyn_cast<ValueDecl>(decl)) assert (isa<ValueDecl>(decl) ||
assert(vd->getFormalAccess() >= AccessLevel::Public); isa<ExtensionDecl>(decl));
else
assert(isa<ExtensionDecl>(decl));
// First, look for local attributes. // First, look for local attributes.
llvm::SetVector<Identifier> spiGroups; llvm::SetVector<Identifier> spiGroups;

View File

@@ -885,7 +885,8 @@ void AttributeChecker::visitSPIAccessControlAttr(SPIAccessControlAttr *attr) {
if (auto VD = dyn_cast<ValueDecl>(D)) { if (auto VD = dyn_cast<ValueDecl>(D)) {
// VD must be public or open to use an @_spi attribute. // VD must be public or open to use an @_spi attribute.
auto declAccess = VD->getFormalAccess(); auto declAccess = VD->getFormalAccess();
if (declAccess < AccessLevel::Public) { if (declAccess < AccessLevel::Public &&
!VD->getAttrs().hasAttribute<UsableFromInlineAttr>()) {
diagnoseAndRemoveAttr(attr, diagnoseAndRemoveAttr(attr,
diag::spi_attribute_on_non_public, diag::spi_attribute_on_non_public,
declAccess, declAccess,

View File

@@ -1,8 +1,11 @@
// Checks for SPI declarations and limited exposability // Checks for SPI declarations and limited exportability.
// RUN: %empty-directory(%t) // RUN: %empty-directory(%t)
// RUN: %target-typecheck-verify-swift -I %t -verify-ignore-unknown -enable-library-evolution -swift-version 5 // RUN: %target-typecheck-verify-swift -I %t -verify-ignore-unknown -enable-library-evolution -swift-version 5
// Without -enable-library-evolution the exportability check looks at struct internal properties.
// RUN: %target-typecheck-verify-swift -I %t -verify-ignore-unknown -swift-version 5
// SPI declarations // SPI declarations
@_spi(MySPI) public func spiFunc() {} // expected-note {{global function 'spiFunc()' is not '@usableFromInline' or public}} @_spi(MySPI) public func spiFunc() {} // expected-note {{global function 'spiFunc()' is not '@usableFromInline' or public}}
@_spi(+) public func invalidSPIName() {} // expected-error {{expected an SPI identifier as subject of the '@_spi' attribute}} @_spi(+) public func invalidSPIName() {} // expected-error {{expected an SPI identifier as subject of the '@_spi' attribute}}
@@ -55,3 +58,26 @@ public struct PublicStructWithProperties {
public var a: SPIClass // expected-error {{cannot use class 'SPIClass' here; it is SPI}} public var a: SPIClass // expected-error {{cannot use class 'SPIClass' here; it is SPI}}
public var b = SPIClass() // expected-error {{cannot use class 'SPIClass' here; it is SPI}} public var b = SPIClass() // expected-error {{cannot use class 'SPIClass' here; it is SPI}}
} }
@_spi(S)
@usableFromInline
func usableFromInlineFunc(_ a: SPIStruct) -> SPIStruct {
fatalError()
}
@_spi(S)
public final class ClassWithUsables {
@usableFromInline
var usableFromInlineVar = SPIClass()
@usableFromInline
func usableFromInlineFunc(_ a: SPIStruct) -> SPIStruct {
fatalError()
}
}
@_spi(S)
public struct NestedParent {
public struct Nested { }
let nested: Nested
}

View File

@@ -241,9 +241,8 @@ extension NormalProtoAssocHolder {
@_spi(AcceptInSPI) @_spi(AcceptInSPI)
@inlinable public func SPIlocalTypeAlias() { @inlinable public func SPIlocalTypeAlias() {
// FIXME these should be accepted. typealias LocalUser = NormalProtoAssocHolder<NormalStruct>
typealias LocalUser = NormalProtoAssocHolder<NormalStruct> // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; the conformance is declared as SPI}} typealias LocalGenericUser<T> = (T, NormalProtoAssocHolder<NormalStruct>)
typealias LocalGenericUser<T> = (T, NormalProtoAssocHolder<NormalStruct>) // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; the conformance is declared as SPI}}
typealias LocalProtoAssoc<T: NormalProto> = T.Assoc typealias LocalProtoAssoc<T: NormalProto> = T.Assoc
_ = LocalProtoAssoc<NormalStruct>() _ = LocalProtoAssoc<NormalStruct>()
@@ -258,9 +257,8 @@ extension NormalProtoAssocHolder {
@_spi(AcceptInSPI) @_spi(AcceptInSPI)
@inlinable public func SPIlocalFunctions() { @inlinable public func SPIlocalFunctions() {
// FIXME these should be accepted. func local(_: NormalProtoAssocHolder<NormalStruct>) {}
func local(_: NormalProtoAssocHolder<NormalStruct>) {} // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; the conformance is declared as SPI}} func localReturn() -> NormalProtoAssocHolder<NormalStruct> { fatalError() }
func localReturn() -> NormalProtoAssocHolder<NormalStruct> { fatalError() } // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; the conformance is declared as SPI}}
let _ = { (_: NormalProtoAssocHolder<NormalStruct>) in return } let _ = { (_: NormalProtoAssocHolder<NormalStruct>) in return }
let _ = { () -> NormalProtoAssocHolder<NormalStruct> in fatalError() } let _ = { () -> NormalProtoAssocHolder<NormalStruct> in fatalError() }
} }