Sema: Introduce an ExportabilityReason for availability attributes.

This allows diagnostics to be more precise and will also support logic that
allows for special cases for `@available` attributes in exportability checking.

Also fixes a bug where the exportability of `@available` attributes attached to
extensions were diagnosed twice for slightly differing reasons.
This commit is contained in:
Allan Shortlidge
2025-09-02 18:31:04 -07:00
parent 95dc7df0c6
commit 91ddf93d0f
5 changed files with 61 additions and 59 deletions

View File

@@ -3826,7 +3826,8 @@ ERROR(decl_from_hidden_module,none,
"as result builder here|"
"in an extension with public or '@usableFromInline' members|"
"in an extension with conditional conformances|"
"in a public or '@usableFromInline' conformance}1; "
"in a public or '@usableFromInline' conformance|"
"in an '@available' attribute here}1; "
"%select{%2 has been imported as implementation-only|"
"it is an SPI imported from %2|"
"it is SPI|"
@@ -3841,7 +3842,8 @@ ERROR(typealias_desugars_to_type_from_hidden_module,none,
"as result builder here|"
"in an extension with public or '@usableFromInline' members|"
"in an extension with conditional conformance|"
"in a public or '@usableFromInline' conformance}3 "
"in a public or '@usableFromInline' conformance|"
"<<ERROR>>}3 "
"because %select{%4 has been imported as implementation-only|"
"it is an SPI imported from %4|"
"<<ERROR>>|"
@@ -3855,7 +3857,7 @@ ERROR(conformance_from_implementation_only_module,none,
"as result builder here|"
"in an extension with public or '@usableFromInline' members|"
"in an extension with conditional conformances|"
"<<ERROR>>}2; "
"<<ERROR>>|<<ERROR>>}2; "
"%select{%3 has been imported as implementation-only|"
"the conformance is declared as SPI in %3|"
"the conformance is declared as SPI|"

View File

@@ -257,6 +257,13 @@ static bool diagnoseValueDeclRefExportability(SourceLoc loc, const ValueDecl *D,
return true;
case ExportabilityReason::Inheritance:
return isa<ProtocolDecl>(D);
case ExportabilityReason::AvailableAttribute:
// If the context is an extension and that extension has an explicit
// access level, then access has already been diagnosed for the
// @available attribute.
if (auto *ED = dyn_cast_or_null<ExtensionDecl>(DC->getAsDecl()))
return !ED->getAttrs().getAttribute<AccessControlAttr>();
return false;
default:
return false;
}

View File

@@ -2261,11 +2261,13 @@ public:
void checkAvailabilityDomains(const Decl *D) {
D = D->getAbstractSyntaxDeclForAttributes();
auto where = Where.withReason(ExportabilityReason::AvailableAttribute);
for (auto attr : D->getSemanticAvailableAttrs()) {
if (auto *domainDecl = attr.getDomain().getDecl()) {
diagnoseDeclAvailability(domainDecl,
attr.getParsedAttr()->getDomainLoc(), nullptr,
Where, std::nullopt);
where, std::nullopt);
}
}
}
@@ -2557,18 +2559,6 @@ public:
: ExportabilityReason::ExtensionWithConditionalConformances;
checkConstrainedExtensionRequirements(ED, reason);
// Diagnose the exportability of the availability domains referenced by the
// @available attributes attached to the extension.
if (Where.isExported()) {
for (auto availableAttr : ED->getSemanticAvailableAttrs()) {
if (auto *domainDecl = availableAttr.getDomain().getDecl()) {
TypeChecker::diagnoseDeclRefExportability(
availableAttr.getParsedAttr()->getDomainLoc(), domainDecl,
Where.withReason(reason));
}
}
}
// If we haven't already visited the extended nominal visit it here.
// This logic is too wide but prevents false reports of an unused public
// import. We should instead check for public generic requirements

View File

@@ -77,7 +77,8 @@ enum class ExportabilityReason : unsigned {
ResultBuilder,
ExtensionWithPublicMembers,
ExtensionWithConditionalConformances,
Inheritance
Inheritance,
AvailableAttribute,
};
/// A description of the restrictions on what declarations can be referenced

View File

@@ -16,11 +16,11 @@
private import Rivers // also re-exported by Oceans
internal import Oceans
// expected-note@-1 22 {{availability domain 'Arctic' imported as 'internal' from 'Oceans' here}}
// expected-note@-1 23 {{availability domain 'Arctic' imported as 'internal' from 'Oceans' here}}
// expected-swift5-note@-2 2 {{availability domain 'Arctic' imported as 'internal' from 'Oceans' here}}
// expected-note@-3 23 {{availability domain 'Colorado' imported as 'internal' from 'Oceans' here}}
// expected-note@-3 24 {{availability domain 'Colorado' imported as 'internal' from 'Oceans' here}}
// expected-swift5-note@-4 2 {{availability domain 'Colorado' imported as 'internal' from 'Oceans' here}}
// expected-note@-5 22 {{availability domain 'Grand' imported as 'internal' from 'Oceans' here}}
// expected-note@-5 23 {{availability domain 'Grand' imported as 'internal' from 'Oceans' here}}
// expected-swift5-note@-6 2 {{availability domain 'Grand' imported as 'internal' from 'Oceans' here}}
public import Seas
@_spiOnly import Lakes
@@ -31,7 +31,7 @@ public import Seas
@available(Arctic) // expected-error {{availability domain 'Arctic' is internal and cannot be used in '@available' on public global function 'publicFunc()'}}
@available(Baltic)
@available(BayBridge)
@available(Salt) // expected-error {{cannot use availability domain 'Salt' here; 'Lakes' was imported for SPI only}}
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an '@available' attribute here; 'Lakes' was imported for SPI only}}
public func publicFunc() { }
@available(Colorado) // expected-swift5-error {{availability domain 'Colorado' used in '@available' on global function 'usableFromInlineFunc()' must be '@usableFromInline' or public}}
@@ -40,7 +40,7 @@ public func publicFunc() { }
@available(Arctic) // expected-swift5-error {{availability domain 'Arctic' used in '@available' on global function 'usableFromInlineFunc()' must be '@usableFromInline' or public}}
@available(Baltic)
@available(BayBridge)
@available(Salt) // expected-error {{cannot use availability domain 'Salt' here; 'Lakes' was imported for SPI only}}
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an '@available' attribute here; 'Lakes' was imported for SPI only}}
@usableFromInline func usableFromInlineFunc() { }
@available(Colorado) // expected-error {{availability domain 'Colorado' is internal and cannot be used in '@available' on public global function 'spiFunc()'}}
@@ -74,7 +74,7 @@ private func privateFunc() { }
@available(Arctic) // expected-error {{availability domain 'Arctic' is internal and cannot be used in '@available' on public var 'publicGlobalVar'}}
@available(Baltic)
@available(BayBridge)
@available(Salt) // expected-error {{cannot use availability domain 'Salt' here; 'Lakes' was imported for SPI only}}
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an '@available' attribute here; 'Lakes' was imported for SPI only}}
public var publicGlobalVar: Int {
get { 0 }
set { }
@@ -86,7 +86,7 @@ public var publicGlobalVar: Int {
@available(Arctic) // expected-error {{availability domain 'Arctic' is internal and cannot be used in '@available' on public struct 'PublicStruct'}}
@available(Baltic)
@available(BayBridge)
@available(Salt) // expected-error {{cannot use availability domain 'Salt' here; 'Lakes' was imported for SPI only}}
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an '@available' attribute here; 'Lakes' was imported for SPI only}}
public struct PublicStruct { }
public struct PublicGenericStruct<T> {
@@ -98,7 +98,7 @@ public struct PublicGenericStruct<T> {
@available(Arctic) // expected-error {{availability domain 'Arctic' is internal and cannot be used in '@available' on public initializer 'init(value:)'}}
@available(Baltic)
@available(BayBridge)
@available(Salt) // expected-error {{cannot use availability domain 'Salt' here; 'Lakes' was imported for SPI only}}
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an '@available' attribute here; 'Lakes' was imported for SPI only}}
public init(value: T) {
self.value = value
}
@@ -109,7 +109,7 @@ public struct PublicGenericStruct<T> {
@available(Arctic) // expected-error {{availability domain 'Arctic' is internal and cannot be used in '@available' on public property 'publicProperty'}}
@available(Baltic)
@available(BayBridge)
@available(Salt) // expected-error {{cannot use availability domain 'Salt' here; 'Lakes' was imported for SPI only}}
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an '@available' attribute here; 'Lakes' was imported for SPI only}}
public var publicProperty: T { value }
@available(Colorado) // expected-swift5-error {{availability domain 'Colorado' used in '@available' on property 'usableFromInlineProperty' must be '@usableFromInline' or public}}
@@ -118,7 +118,7 @@ public struct PublicGenericStruct<T> {
@available(Arctic) // expected-swift5-error {{availability domain 'Arctic' used in '@available' on property 'usableFromInlineProperty' must be '@usableFromInline' or public}}
@available(Baltic)
@available(BayBridge)
@available(Salt) // expected-error {{cannot use availability domain 'Salt' here; 'Lakes' was imported for SPI only}}
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an '@available' attribute here; 'Lakes' was imported for SPI only}}
@usableFromInline var usableFromInlineProperty: T { value }
public var publicPropertyWithSetter: T {
@@ -130,7 +130,7 @@ public struct PublicGenericStruct<T> {
@available(Arctic) // expected-error {{availability domain 'Arctic' is internal and cannot be used in '@available' on public setter for property 'publicPropertyWithSetter'}}
@available(Baltic)
@available(BayBridge)
@available(Salt) // expected-error {{cannot use availability domain 'Salt' here; 'Lakes' was imported for SPI only}}
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an '@available' attribute here; 'Lakes' was imported for SPI only}}
set { value = newValue }
}
@@ -140,7 +140,7 @@ public struct PublicGenericStruct<T> {
@available(Arctic) // expected-error {{availability domain 'Arctic' is internal and cannot be used in '@available' on public instance method 'publicMethod()'}}
@available(Baltic)
@available(BayBridge)
@available(Salt) // expected-error {{cannot use availability domain 'Salt' here; 'Lakes' was imported for SPI only}}
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an '@available' attribute here; 'Lakes' was imported for SPI only}}
public func publicMethod() { }
@available(Colorado) // expected-error {{availability domain 'Colorado' is internal and cannot be used in '@available' on public subscript 'subscript(_:)'}}
@@ -150,7 +150,7 @@ public struct PublicGenericStruct<T> {
@available(Arctic) // expected-error {{availability domain 'Arctic' is internal and cannot be used in '@available' on public subscript 'subscript(_:)'}}
@available(Baltic)
@available(BayBridge)
@available(Salt) // expected-error {{cannot use availability domain 'Salt' here; 'Lakes' was imported for SPI only}}
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an '@available' attribute here; 'Lakes' was imported for SPI only}}
public subscript(indexForSubscriptInColorado: T) -> T { value }
}
@@ -160,7 +160,7 @@ public struct PublicGenericStruct<T> {
@available(Arctic) // expected-error {{availability domain 'Arctic' is internal and cannot be used in '@available' on public enum 'PublicEnum'}}
@available(Baltic)
@available(BayBridge)
@available(Salt) // expected-error {{cannot use availability domain 'Salt' here; 'Lakes' was imported for SPI only}}
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an '@available' attribute here; 'Lakes' was imported for SPI only}}
public enum PublicEnum { }
public enum PublicEnumWithCase {
@@ -170,7 +170,7 @@ public enum PublicEnumWithCase {
@available(Arctic) // expected-error {{availability domain 'Arctic' is internal and cannot be used in '@available' on public enum case 'colorado'}}
@available(Baltic)
@available(BayBridge)
@available(Salt) // expected-error {{cannot use availability domain 'Salt' here; 'Lakes' was imported for SPI only}}
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an '@available' attribute here; 'Lakes' was imported for SPI only}}
case colorado
}
@@ -180,7 +180,7 @@ public enum PublicEnumWithCase {
@available(Arctic) // expected-error {{availability domain 'Arctic' is internal and cannot be used in '@available' on public class 'PublicClass'}}
@available(Baltic)
@available(BayBridge)
@available(Salt) // expected-error {{cannot use availability domain 'Salt' here; 'Lakes' was imported for SPI only}}
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an '@available' attribute here; 'Lakes' was imported for SPI only}}
public class PublicClass { }
@available(Colorado) // expected-error {{availability domain 'Colorado' is internal and cannot be used in '@available' on public protocol 'PublicProtocol'}}
@@ -189,7 +189,7 @@ public class PublicClass { }
@available(Arctic) // expected-error {{availability domain 'Arctic' is internal and cannot be used in '@available' on public protocol 'PublicProtocol'}}
@available(Baltic)
@available(BayBridge)
@available(Salt) // expected-error {{cannot use availability domain 'Salt' here; 'Lakes' was imported for SPI only}}
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an '@available' attribute here; 'Lakes' was imported for SPI only}}
public protocol PublicProtocol { }
public protocol PublicProtocolWithAssociatedType {
@@ -199,7 +199,7 @@ public protocol PublicProtocolWithAssociatedType {
@available(Arctic) // expected-error {{availability domain 'Arctic' is internal and cannot be used in '@available' on public associated type 'AssociatedType'}}
@available(Baltic)
@available(BayBridge)
@available(Salt) // expected-error {{cannot use availability domain 'Salt' here; 'Lakes' was imported for SPI only}}
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an '@available' attribute here; 'Lakes' was imported for SPI only}}
associatedtype AssociatedType
@available(Colorado) // expected-error {{availability domain 'Colorado' is internal and cannot be used in '@available' on public instance method 'requirement()'}}
@@ -208,7 +208,7 @@ public protocol PublicProtocolWithAssociatedType {
@available(Arctic) // expected-error {{availability domain 'Arctic' is internal and cannot be used in '@available' on public instance method 'requirement()'}}
@available(Baltic)
@available(BayBridge)
@available(Salt) // expected-error {{cannot use availability domain 'Salt' here; 'Lakes' was imported for SPI only}}
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an '@available' attribute here; 'Lakes' was imported for SPI only}}
func requirement() -> AssociatedType
}
@@ -218,7 +218,7 @@ public protocol PublicProtocolWithAssociatedType {
@available(Arctic) // expected-error {{availability domain 'Arctic' is internal and cannot be used in '@available' on public type alias 'PublicTypealias'}}
@available(Baltic)
@available(BayBridge)
@available(Salt) // expected-error {{cannot use availability domain 'Salt' here; 'Lakes' was imported for SPI only}}
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an '@available' attribute here; 'Lakes' was imported for SPI only}}
public typealias PublicTypealias = Int
@available(Colorado)
@@ -235,9 +235,20 @@ extension PublicGenericStruct { }
@available(Arctic) // expected-error {{availability domain 'Arctic' is internal and cannot be used in '@available' on public extension of generic struct 'PublicGenericStruct'}}
@available(Baltic)
@available(BayBridge)
@available(Salt) // FIXME: Should be diangosed
@available(Salt)
public extension PublicGenericStruct { }
@available(Colorado) // expected-error {{availability domain 'Colorado' is internal and cannot be used in '@available' on public extension of generic struct 'PublicGenericStruct'}}
@available(Grand) // expected-error {{availability domain 'Grand' is internal and cannot be used in '@available' on public extension of generic struct 'PublicGenericStruct'}}
// expected-warning@-1 {{availability domain 'Grand' is deprecated: Use Colorado instead}}
@available(Arctic) // expected-error {{availability domain 'Arctic' is internal and cannot be used in '@available' on public extension of generic struct 'PublicGenericStruct'}}
@available(Baltic)
@available(BayBridge)
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an '@available' attribute here; 'Lakes' was imported for SPI only}}
public extension PublicGenericStruct {
func publicMemberOfPublicExtension() { }
}
@available(Colorado)
@available(Grand) // expected-warning {{availability domain 'Grand' is deprecated: Use Colorado instead}}
@available(Arctic)
@@ -248,15 +259,12 @@ extension PublicGenericStruct {
func internalMethodInExtensionInColorado() { }
}
@available(Colorado) // expected-error {{cannot use availability domain 'Colorado' in an extension with public or '@usableFromInline' members; 'Rivers' was not imported publicly}}
@available(Grand) // expected-error {{cannot use availability domain 'Grand' in an extension with public or '@usableFromInline' members; 'Rivers' was not imported publicly}}
// expected-warning@-1 {{availability domain 'Grand' is deprecated: Use Colorado instead}}
@available(Arctic) // expected-error {{cannot use availability domain 'Arctic' in an extension with public or '@usableFromInline' members; 'Oceans' was not imported publicly}}
@available(Colorado) // expected-error {{cannot use availability domain 'Colorado' in an '@available' attribute here; 'Rivers' was not imported publicly}}
@available(Grand) // expected-error {{cannot use availability domain 'Grand' in an '@available' attribute here; 'Rivers' was not imported publicly}}
@available(Arctic) // expected-error {{cannot use availability domain 'Arctic' in an '@available' attribute here; 'Oceans' was not imported publicly}}
@available(Baltic)
@available(BayBridge)
// FIXME: Duplicate error
@available(Salt) // expected-error {{cannot use availability domain 'Salt' here; 'Lakes' was imported for SPI only}}
// expected-error@-1 {{cannot use availability domain 'Salt' in an extension with public or '@usableFromInline' members; 'Lakes' was imported for SPI only}}
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an '@available' attribute here; 'Lakes' was imported for SPI only}}
extension PublicGenericStruct {
public func publicMethodInExtensionInColorado() { }
}
@@ -269,26 +277,20 @@ extension PublicGenericStruct {
@available(Salt)
extension PublicGenericStruct where T: PublicProtocolWithAssociatedType { }
@available(Colorado) // expected-error {{cannot use availability domain 'Colorado' in an extension with conditional conformances; 'Rivers' was not imported publicly}}
@available(Grand) // expected-error {{cannot use availability domain 'Grand' in an extension with conditional conformances; 'Rivers' was not imported publicly}}
// expected-warning@-1 {{availability domain 'Grand' is deprecated: Use Colorado instead}}
@available(Arctic) // expected-error {{cannot use availability domain 'Arctic' in an extension with conditional conformances; 'Oceans' was not imported publicly}}
@available(Colorado) // expected-error {{cannot use availability domain 'Colorado' in an '@available' attribute here; 'Rivers' was not imported publicly}}
@available(Grand) // expected-error {{cannot use availability domain 'Grand' in an '@available' attribute here; 'Rivers' was not imported publicly}}
@available(Arctic) // expected-error {{cannot use availability domain 'Arctic' in an '@available' attribute here; 'Oceans' was not imported publicly}}
@available(Baltic)
@available(BayBridge)
// FIXME: Duplicate error
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an extension with conditional conformances; 'Lakes' was imported for SPI only}}
// expected-error@-1 {{cannot use availability domain 'Salt' here; 'Lakes' was imported for SPI only}}
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an '@available' attribute here; 'Lakes' was imported for SPI only}}
extension PublicGenericStruct: PublicProtocol {}
@available(Colorado) // expected-error {{cannot use availability domain 'Colorado' in an extension with public or '@usableFromInline' members; 'Rivers' was not imported publicly}}
@available(Grand) // expected-error {{cannot use availability domain 'Grand' in an extension with public or '@usableFromInline' members; 'Rivers' was not imported publicly}}
// expected-warning@-1 {{availability domain 'Grand' is deprecated: Use Colorado instead}}
@available(Arctic) // expected-error {{cannot use availability domain 'Arctic' in an extension with public or '@usableFromInline' members; 'Oceans' was not imported publicly}}
@available(Colorado) // expected-error {{cannot use availability domain 'Colorado' in an '@available' attribute here; 'Rivers' was not imported publicly}}
@available(Grand) // expected-error {{cannot use availability domain 'Grand' in an '@available' attribute here; 'Rivers' was not imported publicly}}
@available(Arctic) // expected-error {{cannot use availability domain 'Arctic' in an '@available' attribute here; 'Oceans' was not imported publicly}}
@available(Baltic)
@available(BayBridge)
// FIXME: Duplicate error
@available(Salt) // expected-error {{cannot use availability domain 'Salt' here; 'Lakes' was imported for SPI only}}
// expected-error@-1 {{cannot use availability domain 'Salt' in an extension with public or '@usableFromInline' members; 'Lakes' was imported for SPI only}}
@available(Salt) // expected-error {{cannot use availability domain 'Salt' in an '@available' attribute here; 'Lakes' was imported for SPI only}}
extension PublicGenericStruct: PublicProtocolWithAssociatedType {
public func requirement() -> Int { 0 }
}