Skip to content

Commit c8cfe8f

Browse files
authored
Merge pull request #76403 from tshortli/availability-attr-diags
Sema: Reject unavailable associated types and observing accessors
2 parents 3b741ee + 21837e9 commit c8cfe8f

9 files changed

+83
-28
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6757,12 +6757,13 @@ WARNING(availability_query_useless_enclosing_scope, none,
67576757
NOTE(availability_query_useless_enclosing_scope_here, none,
67586758
"enclosing scope here", ())
67596759

6760-
ERROR(availability_deinit_no_potential, none,
6761-
"deinitializer cannot be marked potentially unavailable with "
6762-
"'@available'", ())
6760+
ERROR(availability_decl_no_potential, none,
6761+
"%kindonly0 cannot be marked potentially unavailable with '@available'",
6762+
(const Decl *))
67636763

6764-
ERROR(availability_deinit_no_unavailable, none,
6765-
"deinitializer cannot be marked unavailable with '@available'", ())
6764+
ERROR(availability_decl_no_unavailable, none,
6765+
"%kindonly0 cannot be marked unavailable with '@available'",
6766+
(const Decl *))
67666767

67676768
ERROR(availability_global_script_no_potential,
67686769
none, "global variable cannot be marked potentially "

lib/IDE/SyntaxModel.cpp

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -358,21 +358,6 @@ class ModelASTWalker : public ASTWalker {
358358
/// is considered as one, e.g. object literal expression.
359359
uint8_t AvoidPassingSyntaxToken = 0;
360360

361-
class InactiveClauseRAII {
362-
const bool wasInInactiveClause;
363-
bool &isInInactiveClause;
364-
365-
public:
366-
InactiveClauseRAII(bool &isInInactiveClauseArg, bool enteringInactiveClause)
367-
: wasInInactiveClause(isInInactiveClauseArg),
368-
isInInactiveClause(isInInactiveClauseArg) {
369-
isInInactiveClause |= enteringInactiveClause;
370-
}
371-
~InactiveClauseRAII() { isInInactiveClause = wasInInactiveClause; }
372-
};
373-
friend class InactiveClauseRAII;
374-
bool inInactiveClause = false;
375-
376361
public:
377362
SyntaxModelWalker &Walker;
378363
ArrayRef<SyntaxNode> TokenNodes;

lib/Sema/TypeCheckAttr.cpp

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2220,7 +2220,7 @@ void AttributeChecker::visitAvailableAttr(AvailableAttr *attr) {
22202220
}
22212221
}
22222222

2223-
std::optional<Diag<>> MaybeNotAllowed =
2223+
std::optional<Diagnostic> MaybeNotAllowed =
22242224
TypeChecker::diagnosticIfDeclCannotBePotentiallyUnavailable(D);
22252225
if (MaybeNotAllowed.has_value()) {
22262226
AvailabilityContext DeploymentRange
@@ -4924,13 +4924,19 @@ Type TypeChecker::checkReferenceOwnershipAttr(VarDecl *var, Type type,
49244924
return ReferenceStorageType::get(type, ownershipKind, var->getASTContext());
49254925
}
49264926

4927-
std::optional<Diag<>>
4927+
std::optional<Diagnostic>
49284928
TypeChecker::diagnosticIfDeclCannotBePotentiallyUnavailable(const Decl *D) {
49294929
auto *DC = D->getDeclContext();
49304930

49314931
// A destructor is always called if declared.
49324932
if (auto *DD = dyn_cast<DestructorDecl>(D))
4933-
return diag::availability_deinit_no_potential;
4933+
return Diagnostic(diag::availability_decl_no_potential, D);
4934+
4935+
// Observing accessors are always called implicitly.
4936+
if (auto *AD = dyn_cast<AccessorDecl>(D)) {
4937+
if (AD->isObservingAccessor())
4938+
return Diagnostic(diag::availability_decl_no_potential, D);
4939+
}
49344940

49354941
if (auto *VD = dyn_cast<VarDecl>(D)) {
49364942
if (!VD->hasStorageOrWrapsStorage())
@@ -4964,7 +4970,7 @@ TypeChecker::diagnosticIfDeclCannotBePotentiallyUnavailable(const Decl *D) {
49644970
return std::nullopt;
49654971
}
49664972

4967-
std::optional<Diag<>>
4973+
std::optional<Diagnostic>
49684974
TypeChecker::diagnosticIfDeclCannotBeUnavailable(const Decl *D) {
49694975
auto parentIsUnavailable = [](const Decl *D) -> bool {
49704976
if (auto *parent =
@@ -4979,7 +4985,18 @@ TypeChecker::diagnosticIfDeclCannotBeUnavailable(const Decl *D) {
49794985
if (parentIsUnavailable(D))
49804986
return std::nullopt;
49814987

4982-
return diag::availability_deinit_no_unavailable;
4988+
return Diagnostic(diag::availability_decl_no_unavailable, D);
4989+
}
4990+
4991+
// The conformance checker does not know what to do with unavailable
4992+
// associated types.
4993+
if (auto *AT = dyn_cast<AssociatedTypeDecl>(D))
4994+
return Diagnostic(diag::availability_decl_no_unavailable, D);
4995+
4996+
// Observing accessors are always called implicitly.
4997+
if (auto *AD = dyn_cast<AccessorDecl>(D)) {
4998+
if (AD->isObservingAccessor())
4999+
return Diagnostic(diag::availability_decl_no_unavailable, D);
49835000
}
49845001

49855002
if (auto *VD = dyn_cast<VarDecl>(D)) {

lib/Sema/TypeChecker.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,13 +1020,13 @@ TypeRefinementContext *getOrBuildTypeRefinementContext(SourceFile *SF);
10201020
/// Returns a diagnostic indicating why the declaration cannot be annotated
10211021
/// with an @available() attribute indicating it is potentially unavailable
10221022
/// or None if this is allowed.
1023-
std::optional<Diag<>>
1023+
std::optional<Diagnostic>
10241024
diagnosticIfDeclCannotBePotentiallyUnavailable(const Decl *D);
10251025

10261026
/// Returns a diagnostic indicating why the declaration cannot be annotated
10271027
/// with an @available() attribute indicating it is unavailable or None if this
10281028
/// is allowed.
1029-
std::optional<Diag<>> diagnosticIfDeclCannotBeUnavailable(const Decl *D);
1029+
std::optional<Diagnostic> diagnosticIfDeclCannotBeUnavailable(const Decl *D);
10301030

10311031
/// Same as \c checkDeclarationAvailability but doesn't give a reason for
10321032
/// unavailability.

test/Sema/availability_accessors.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ struct SubscriptHelper {
9898
@available(*, unavailable)
9999
set { } // expected-note {{setter for 'subscript(unavailableGetterAndSetter:)' has been explicitly marked unavailable here}}
100100
}
101-
102101
}
103102

104103
@discardableResult func takesInOut<T>(_ t: inout T) -> T {
@@ -553,3 +552,22 @@ struct TestPatternBindingInitExprs {
553552
var unavailableGetterAndSetter_0 = global.unavailableGetterAndSetter[0] // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}}
554553
var unavailableGetterAndSetter_0_b = global.unavailableGetterAndSetter[0].b // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}}
555554
}
555+
556+
struct BadAccessorAvailability<T> {
557+
var alwaysUnavailableObservers: T {
558+
@available(*, unavailable) // expected-error {{willSet observer for property cannot be marked unavailable with '@available'}}
559+
willSet { }
560+
561+
@available(*, unavailable) // expected-error {{didSet observer for property cannot be marked unavailable with '@available'}}
562+
didSet { }
563+
}
564+
565+
var observersUnavailableBeforeSwift99: T {
566+
@available(swift, introduced: 99) // expected-error {{willSet observer for property cannot be marked unavailable with '@available'}}
567+
willSet { }
568+
569+
@available(swift, introduced: 99) // expected-error {{didSet observer for property cannot be marked unavailable with '@available'}}
570+
didSet { }
571+
}
572+
573+
}

test/Sema/availability_versions.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1801,3 +1801,21 @@ class StoredPropertiesWithAvailabilityInClosures {
18011801
return 0
18021802
}()
18031803
}
1804+
1805+
struct PropertyObservers {
1806+
var hasPotentiallyUnavailableObservers: Int {
1807+
@available(macOS 51, *) // expected-error {{willSet observer for property cannot be marked potentially unavailable with '@available'}}
1808+
willSet { }
1809+
1810+
@available(macOS 51, *) // expected-error {{didSet observer for property cannot be marked potentially unavailable with '@available'}}
1811+
didSet { }
1812+
}
1813+
1814+
var hasObsoletedObservers: Int {
1815+
@available(macOS, obsoleted: 10.9) // expected-error {{willSet observer for property cannot be marked unavailable with '@available'}}
1816+
willSet { }
1817+
1818+
@available(macOS, obsoleted: 10.9) // expected-error {{didSet observer for property cannot be marked unavailable with '@available'}}
1819+
didSet { }
1820+
}
1821+
}

test/decl/protocol/associated_type_availability.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,11 @@ extension ModelP2 {
8484
// expected-error@-1{{'B' is only available in macOS 14 or newer}}
8585
// expected-note@-2{{add @available attribute to enclosing instance method}}
8686
}
87+
88+
protocol P3 {
89+
@available(macOS, obsoleted: 12) // expected-error{{associated type cannot be marked unavailable with '@available'}}
90+
associatedtype A1
91+
92+
@available(macOS, obsoleted: 99) // FIXME: this should probably be diagnosed
93+
associatedtype A2
94+
}

test/decl/protocol/req/unavailable.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,11 @@ struct SelfT: Unavail { // expected-error {{type 'SelfT' does not conform to pro
9191
// expected-error@-1 {{unavailable instance method 'req()' was used to satisfy a requirement of protocol 'Unavail': write it yourself}}
9292
typealias T = SelfT
9393
}
94+
95+
protocol UnavailableAssoc {
96+
@available(*, unavailable) // expected-error {{associated type cannot be marked unavailable with '@available'}}
97+
associatedtype A1
98+
99+
@available(swift, introduced: 99) // expected-error {{associated type cannot be marked unavailable with '@available'}}
100+
associatedtype A2
101+
}

0 commit comments

Comments
 (0)