Skip to content

Sema: Loosen more available than enclosing extension diagnostic #79122

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
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
7 changes: 6 additions & 1 deletion include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1437,8 +1437,13 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi

/// Returns the active platform-specific `@available` attribute that should be
/// used to determine the platform introduction version of the decl.
///
/// If the declaration does not have a platform introduction attribute of its
/// own and is a member of an extension then the platform introduction
/// attribute attached to the extension will be returned instead unless \p
/// checkExtension is false.
std::optional<SemanticAvailableAttr>
getAvailableAttrForPlatformIntroduction() const;
getAvailableAttrForPlatformIntroduction(bool checkExtension = true) const;

/// Returns true if the declaration is deprecated at the current deployment
/// target.
Expand Down
4 changes: 3 additions & 1 deletion lib/AST/Availability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,7 @@ AvailabilityRange AvailabilityInference::annotatedAvailableRangeForAttr(
}

std::optional<SemanticAvailableAttr>
Decl::getAvailableAttrForPlatformIntroduction() const {
Decl::getAvailableAttrForPlatformIntroduction(bool checkExtension) const {
if (auto attr = getDeclAvailableAttrForPlatformIntroduction(this))
return attr;

Expand All @@ -804,6 +804,8 @@ Decl::getAvailableAttrForPlatformIntroduction() const {
// if the declaration does not have an explicit @available attribute
// itself. This check relies on the fact that we cannot have nested
// extensions.
if (!checkExtension)
return std::nullopt;

if (auto parent =
AvailabilityInference::parentDeclForInferredAvailability(this)) {
Expand Down
9 changes: 2 additions & 7 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2338,7 +2338,8 @@ static Decl *getEnclosingDeclForDecl(Decl *D) {

static std::optional<std::pair<SemanticAvailableAttr, const Decl *>>
getSemanticAvailableRangeDeclAndAttr(const Decl *decl) {
if (auto attr = decl->getAvailableAttrForPlatformIntroduction())
if (auto attr = decl->getAvailableAttrForPlatformIntroduction(
/*checkExtension=*/false))
return std::make_pair(*attr, decl);

if (auto *parent =
Expand Down Expand Up @@ -2467,12 +2468,6 @@ void AttributeChecker::visitAvailableAttr(AvailableAttr *parsedAttr) {
// not diagnosed previously, so only emit a warning in that case.
if (isa<ExtensionDecl>(DC->getTopmostDeclarationDeclContext()))
limit = DiagnosticBehavior::Warning;
} else if (enclosingAttr.getPlatform() != attr->getPlatform()) {
// Downgrade to a warning when the limiting attribute is for a more
// specific platform.
if (inheritsAvailabilityFromPlatform(enclosingAttr.getPlatform(),
attr->getPlatform()))
limit = DiagnosticBehavior::Warning;
}
diagnose(D->isImplicit() ? enclosingDecl->getLoc()
: parsedAttr->getLocation(),
Expand Down
7 changes: 6 additions & 1 deletion test/Sema/availability_versions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1184,9 +1184,14 @@ extension ClassToExtend : ProtocolAvailableOn51 {
}

@available(OSX, introduced: 51)
extension ClassToExtend { // expected-note {{enclosing scope requires availability of macOS 51 or newer}}
extension ClassToExtend { // expected-note 2 {{enclosing scope requires availability of macOS 51 or newer}}
@available(OSX, introduced: 10.9) // expected-error {{instance method cannot be more available than enclosing scope}}
func extensionMethod10_9() { }

struct Nested {
@available(OSX, introduced: 10.9) // expected-warning {{instance method cannot be more available than enclosing scope}}
func nestedTypeMethod10_9() { }
}
}

func usePotentiallyUnavailableExtension() {
Expand Down
43 changes: 41 additions & 2 deletions test/attr/attr_availability_maccatalyst.swift
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,8 @@ public extension AvailableOnMacCatalyst { } // ok

@available(iOS, introduced: 14.0)
@available(macCatalyst, introduced: 14.5)
public struct AvailableLaterOnMacCatalyst { // expected-note {{enclosing scope requires availability of Mac Catalyst 14.5 or newer}}
@available(iOS, introduced: 14.0) // expected-warning {{instance method cannot be more available than enclosing scope}}
public struct AvailableLaterOnMacCatalyst { // expected-note 2 {{enclosing scope requires availability of Mac Catalyst 14.5 or newer}}
@available(iOS, introduced: 14.0) // expected-error {{instance method cannot be more available than enclosing scope}}
func iOSOnly() { }

@available(macCatalyst, introduced: 14.5)
Expand All @@ -177,4 +177,43 @@ public struct AvailableLaterOnMacCatalyst { // expected-note {{enclosing scope r
@available(iOS, introduced: 14.0)
@available(macCatalyst, introduced: 14.5)
func iOSAndMacCatalyst() { }

struct Nested {
@available(iOS, introduced: 14.0) // expected-error {{instance method cannot be more available than enclosing scope}}
func iOSOnlyNested() { }

@available(macCatalyst, introduced: 14.5)
func macCatalystOnlyNested() { }

@available(iOS, introduced: 14.0)
@available(macCatalyst, introduced: 14.5)
func iOSAndMacCatalystNested() { }
}
}

@available(iOS, introduced: 14.0)
@available(macCatalyst, introduced: 14.5)
extension AvailableLaterOnMacCatalyst { // expected-note 2 {{enclosing scope requires availability of Mac Catalyst 14.5 or newer}}
@available(iOS, introduced: 14.0) // expected-error {{instance method cannot be more available than enclosing scope}}
func iOSOnlyInExtension() { }

@available(macCatalyst, introduced: 14.5)
func macCatalystOnlyInExtension() { }

@available(iOS, introduced: 14.0)
@available(macCatalyst, introduced: 14.5)
func iOSAndMacCatalystInExtension() { }

struct NestedInExtension {
@available(iOS, introduced: 14.0) // expected-warning {{instance method cannot be more available than enclosing scope}}
func iOSOnlyNestedInExtension() { }

@available(macCatalyst, introduced: 14.5)
func macCatalystOnlyNestedInExtension() { }

@available(iOS, introduced: 14.0)
@available(macCatalyst, introduced: 14.5)
func iOSAndMacCatalystNestedInExtension() { }
}

}