Skip to content

[Property Wrappers] Fix availability inference of synthesized property wrapper setters #36711

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
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
3 changes: 3 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,9 @@ class alignas(1 << DeclAlignInBits) Decl {
return Attrs;
}

/// Returns the innermost enclosing decl with an availability annotation.
const Decl *getInnermostDeclWithAvailability() const;

/// Returns the introduced OS version in the given platform kind specified
/// by @available attribute.
/// This function won't consider the parent context to get the information.
Expand Down
13 changes: 13 additions & 0 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,19 @@ StringRef Decl::getDescriptiveKindName(DescriptiveDeclKind K) {
llvm_unreachable("bad DescriptiveDeclKind");
}

const Decl *Decl::getInnermostDeclWithAvailability() const {
const Decl *enclosingDecl = this;
// Find the innermost enclosing declaration with an @available annotation.
while (enclosingDecl != nullptr) {
if (enclosingDecl->getAttrs().hasAttribute<AvailableAttr>())
return enclosingDecl;

enclosingDecl = enclosingDecl->getDeclContext()->getAsDecl();
}

return nullptr;
}

Optional<llvm::VersionTuple>
Decl::getIntroducedOSVersion(PlatformKind Kind) const {
for (auto *attr: getAttrs()) {
Expand Down
6 changes: 2 additions & 4 deletions lib/Sema/CodeSynthesis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,17 +566,15 @@ configureInheritedDesignatedInitAttributes(ClassDecl *classDecl,
// If the superclass has its own availability, make sure the synthesized
// constructor is only as available as its superclass's constructor.
if (superclassCtor->getAttrs().hasAttribute<AvailableAttr>()) {
SmallVector<Decl *, 2> asAvailableAs;
SmallVector<const Decl *, 2> asAvailableAs;

// We don't have to look at enclosing contexts of the superclass constructor,
// because designated initializers must always be defined in the superclass
// body, and we already enforce that a superclass is at least as available as
// a subclass.
asAvailableAs.push_back(superclassCtor);
Decl *parentDecl = classDecl;
while (parentDecl != nullptr) {
if (auto *parentDecl = classDecl->getInnermostDeclWithAvailability()) {
asAvailableAs.push_back(parentDecl);
parentDecl = parentDecl->getDeclContext()->getAsDecl();
}
AvailabilityInference::applyInferredAvailableAttrs(
ctor, asAvailableAs, ctx);
Expand Down
79 changes: 46 additions & 33 deletions lib/Sema/TypeCheckStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1926,6 +1926,45 @@ static AccessorDecl *createGetterPrototype(AbstractStorageDecl *storage,
return getter;
}

static void addPropertyWrapperAccessorAvailability(VarDecl *var, AccessorKind accessorKind,
SmallVectorImpl<const Decl *> &asAvailableAs) {
AccessorDecl *synthesizedFrom = nullptr;
if (var->hasAttachedPropertyWrapper()) {
// The property wrapper info may not actually link back to a wrapper
// implementation, if there was a semantic error checking the wrapper.
auto info = var->getAttachedPropertyWrapperTypeInfo(0);
if (info.valueVar) {
synthesizedFrom = info.valueVar->getOpaqueAccessor(accessorKind);
}
} else if (auto wrapperSynthesizedKind
= var->getPropertyWrapperSynthesizedPropertyKind()) {
switch (*wrapperSynthesizedKind) {
case PropertyWrapperSynthesizedPropertyKind::Backing:
break;

case PropertyWrapperSynthesizedPropertyKind::Projection: {
if (auto origVar = var->getOriginalWrappedProperty(wrapperSynthesizedKind)) {
// The property wrapper info may not actually link back to a wrapper
// implementation, if there was a semantic error checking the wrapper.
auto info = origVar->getAttachedPropertyWrapperTypeInfo(0);
if (info.projectedValueVar) {
synthesizedFrom = info.projectedValueVar->getOpaqueAccessor(accessorKind);
}
}
break;
}
}
}

// Infer availability from the accessor used for synthesis, and intersect it
// with the availability of the enclosing scope.
if (synthesizedFrom) {
asAvailableAs.push_back(synthesizedFrom);
if (auto *enclosingDecl = var->getInnermostDeclWithAvailability())
asAvailableAs.push_back(enclosingDecl);
}
}

static AccessorDecl *createSetterPrototype(AbstractStorageDecl *storage,
ASTContext &ctx,
AccessorDecl *getter = nullptr) {
Expand Down Expand Up @@ -1967,41 +2006,11 @@ static AccessorDecl *createSetterPrototype(AbstractStorageDecl *storage,
assert(storage->requiresOpaqueAccessor(AccessorKind::Set));

// Copy availability from the accessor we'll synthesize the setter from.
SmallVector<Decl *, 2> asAvailableAs;
SmallVector<const Decl *, 2> asAvailableAs;

// That could be a property wrapper...
if (auto var = dyn_cast<VarDecl>(storage)) {
if (var->hasAttachedPropertyWrapper()) {
// The property wrapper info may not actually link back to a wrapper
// implementation, if there was a semantic error checking the wrapper.
auto info = var->getAttachedPropertyWrapperTypeInfo(0);
if (info.valueVar) {
if (auto setter = info.valueVar->getOpaqueAccessor(AccessorKind::Set)) {
asAvailableAs.push_back(setter);
}
}
} else if (auto wrapperSynthesizedKind
= var->getPropertyWrapperSynthesizedPropertyKind()) {
switch (*wrapperSynthesizedKind) {
case PropertyWrapperSynthesizedPropertyKind::Backing:
break;

case PropertyWrapperSynthesizedPropertyKind::Projection: {
if (auto origVar = var->getOriginalWrappedProperty(wrapperSynthesizedKind)) {
// The property wrapper info may not actually link back to a wrapper
// implementation, if there was a semantic error checking the wrapper.
auto info = origVar->getAttachedPropertyWrapperTypeInfo(0);
if (info.projectedValueVar) {
if (auto setter
= info.projectedValueVar->getOpaqueAccessor(AccessorKind::Set)){
asAvailableAs.push_back(setter);
}
}
}
break;
}
}
}
addPropertyWrapperAccessorAvailability(var, AccessorKind::Set, asAvailableAs);
}


Expand Down Expand Up @@ -2095,6 +2104,10 @@ createCoroutineAccessorPrototype(AbstractStorageDecl *storage,
}
}

if (auto var = dyn_cast<VarDecl>(storage)) {
addPropertyWrapperAccessorAvailability(var, kind, asAvailableAs);
}

AvailabilityInference::applyInferredAvailableAttrs(accessor,
asAvailableAs, ctx);

Expand Down
43 changes: 43 additions & 0 deletions test/Sema/generalized_accessors_availability.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,46 @@ func butt(x: inout Butt) { // expected-note*{{}}
x.$wrapped_modify_conditionally_available = 0
}
}

@available(macOS 11.0, *)
struct LessAvailable {
@SetterConditionallyAvailable
var wrapped_setter_more_available: Int

@ModifyConditionallyAvailable
var wrapped_modify_more_available: Int

var nested: Nested

struct Nested {
@SetterConditionallyAvailable
var wrapped_setter_more_available: Int

@ModifyConditionallyAvailable
var wrapped_modify_more_available: Int
}
}

func testInferredAvailability(x: inout LessAvailable) { // expected-error {{'LessAvailable' is only available in macOS 11.0 or newer}} expected-note*{{}}
x.wrapped_setter_more_available = 0 // expected-error {{setter for 'wrapped_setter_more_available' is only available in macOS 11.0 or newer}} expected-note{{}}
x.wrapped_modify_more_available = 0 // expected-error {{setter for 'wrapped_modify_more_available' is only available in macOS 11.0 or newer}} expected-note{{}}
x.$wrapped_setter_more_available = 0 // expected-error {{setter for '$wrapped_setter_more_available' is only available in macOS 11.0 or newer}} expected-note{{}}
x.$wrapped_modify_more_available = 0 // expected-error {{setter for '$wrapped_modify_more_available' is only available in macOS 11.0 or newer}} expected-note{{}}

x.nested.wrapped_setter_more_available = 0 // expected-error {{setter for 'wrapped_setter_more_available' is only available in macOS 11.0 or newer}} expected-note{{}}
x.nested.wrapped_modify_more_available = 0 // expected-error {{setter for 'wrapped_modify_more_available' is only available in macOS 11.0 or newer}} expected-note{{}}
x.nested.$wrapped_setter_more_available = 0 // expected-error {{setter for '$wrapped_setter_more_available' is only available in macOS 11.0 or newer}} expected-note{{}}
x.nested.$wrapped_modify_more_available = 0 // expected-error {{setter for '$wrapped_modify_more_available' is only available in macOS 11.0 or newer}} expected-note{{}}

if #available(macOS 11.0, *) {
x.wrapped_setter_more_available = 0
x.wrapped_modify_more_available = 0
x.$wrapped_setter_more_available = 0
x.$wrapped_modify_more_available = 0

x.nested.wrapped_setter_more_available = 0
x.nested.wrapped_modify_more_available = 0
x.nested.$wrapped_setter_more_available = 0
x.nested.$wrapped_modify_more_available = 0
}
}