Skip to content

Commit 3f5dec5

Browse files
authored
Merge pull request #61028 from xedin/type-wrapper-ignore-attr
[AST/TypeChecker] TypeWrappers: Add @typeWrapperIgnored attribute
2 parents d043378 + 53bcf9f commit 3f5dec5

File tree

10 files changed

+165
-5
lines changed

10 files changed

+165
-5
lines changed

include/swift/AST/Attr.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,11 @@ DECL_ATTR(_documentation, Documentation,
777777
APIBreakingToAdd | APIStableToRemove | ABIStableToAdd | ABIStableToRemove,
778778
136)
779779

780+
SIMPLE_DECL_ATTR(typeWrapperIgnored, TypeWrapperIgnored,
781+
OnVar |
782+
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
783+
137)
784+
780785
// If you're adding a new underscored attribute here, please document it in
781786
// docs/ReferenceGuides/UnderscoredAttributes.md.
782787

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6568,5 +6568,14 @@ ERROR(type_wrapper_type_requirement_not_accessible,none,
65686568
"(which is %select{private|fileprivate|internal|public|open}4)",
65696569
(AccessLevel, DescriptiveDeclKind, DeclName, Type, AccessLevel))
65706570

6571+
ERROR(type_wrapper_ignored_on_local_properties,none,
6572+
"%0 must not be used on local properties", (DeclAttribute))
6573+
ERROR(type_wrapper_ignored_on_computed_properties,none,
6574+
"%0 must not be used on computed properties", (DeclAttribute))
6575+
ERROR(type_wrapper_ignored_on_static_properties,none,
6576+
"%0 must not be used on static properties", (DeclAttribute))
6577+
ERROR(type_wrapper_ignored_on_lazy_properties,none,
6578+
"%0 must not be used on lazy properties", (DeclAttribute))
6579+
65716580
#define UNDEFINE_DIAGNOSTIC_MACROS
65726581
#include "DefineDiagnosticMacros.h"

lib/Sema/CodeSynthesis.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -385,8 +385,12 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
385385
if (var->isImplicit())
386386
continue;
387387

388-
// Computed properties are not included.
389-
if (!var->hasStorage())
388+
// Computed properties are not included, except in cases
389+
// where property has a property wrapper and `@typeWrapperIgnored`
390+
// attribute.
391+
if (!var->hasStorage() &&
392+
!(var->hasAttachedPropertyWrapper() &&
393+
var->getAttrs().hasAttribute<TypeWrapperIgnoredAttr>()))
390394
continue;
391395

392396
// If this is a memberwise initializeable property include

lib/Sema/TypeCheckAttr.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
294294
void visitCustomAttr(CustomAttr *attr);
295295
void visitPropertyWrapperAttr(PropertyWrapperAttr *attr);
296296
void visitTypeWrapperAttr(TypeWrapperAttr *attr);
297+
void visitTypeWrapperIgnoredAttr(TypeWrapperIgnoredAttr *attr);
297298
void visitResultBuilderAttr(ResultBuilderAttr *attr);
298299

299300
void visitImplementationOnlyAttr(ImplementationOnlyAttr *attr);
@@ -3796,6 +3797,46 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
37963797
}
37973798
}
37983799

3800+
void AttributeChecker::visitTypeWrapperIgnoredAttr(TypeWrapperIgnoredAttr *attr) {
3801+
auto *var = cast<VarDecl>(D);
3802+
3803+
// @typeWrapperIgnored applies only to properties that type wrapper can manage.
3804+
if (var->getDeclContext()->isLocalContext()) {
3805+
diagnoseAndRemoveAttr(attr, diag::type_wrapper_ignored_on_local_properties, attr);
3806+
return;
3807+
}
3808+
3809+
if (var->isLet()) {
3810+
diagnoseAndRemoveAttr(attr, diag::attr_only_one_decl_kind, attr, "var");
3811+
return;
3812+
}
3813+
3814+
if (var->getAttrs().hasAttribute<LazyAttr>()) {
3815+
diagnoseAndRemoveAttr(attr, diag::type_wrapper_ignored_on_lazy_properties, attr);
3816+
return;
3817+
}
3818+
3819+
if (var->isStatic()) {
3820+
diagnoseAndRemoveAttr(attr, diag::type_wrapper_ignored_on_static_properties, attr);
3821+
return;
3822+
}
3823+
3824+
// computed properties
3825+
{
3826+
SmallVector<AccessorKind, 4> accessors{AccessorKind::Get, AccessorKind::Set,
3827+
AccessorKind::Modify,
3828+
AccessorKind::MutableAddress};
3829+
3830+
if (llvm::any_of(accessors, [&var](const auto &accessor) {
3831+
return var->getParsedAccessor(accessor);
3832+
})) {
3833+
diagnoseAndRemoveAttr(
3834+
attr, diag::type_wrapper_ignored_on_computed_properties, attr);
3835+
return;
3836+
}
3837+
}
3838+
}
3839+
37993840
void AttributeChecker::visitResultBuilderAttr(ResultBuilderAttr *attr) {
38003841
auto *nominal = dyn_cast<NominalTypeDecl>(D);
38013842
auto &ctx = D->getASTContext();

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1587,6 +1587,7 @@ namespace {
15871587
UNINTERESTING_ATTR(Custom)
15881588
UNINTERESTING_ATTR(PropertyWrapper)
15891589
UNINTERESTING_ATTR(TypeWrapper)
1590+
UNINTERESTING_ATTR(TypeWrapperIgnored)
15901591
UNINTERESTING_ATTR(DisfavoredOverload)
15911592
UNINTERESTING_ATTR(ResultBuilder)
15921593
UNINTERESTING_ATTR(ProjectedValueProperty)

lib/Sema/TypeCheckTypeWrapper.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,11 @@ bool IsPropertyAccessedViaTypeWrapper::evaluate(Evaluator &evaluator,
315315
if (property->isStatic() || property->isLet())
316316
return false;
317317

318+
// If this property has `@typeWrapperIgnored` attribute
319+
// it should not be managed by a type wrapper.
320+
if (property->getAttrs().hasAttribute<TypeWrapperIgnoredAttr>())
321+
return false;
322+
318323
// `lazy` properties are not wrapped.
319324
if (property->getAttrs().hasAttribute<LazyAttr>() ||
320325
property->isLazyStorageProperty())
@@ -337,9 +342,13 @@ bool IsPropertyAccessedViaTypeWrapper::evaluate(Evaluator &evaluator,
337342
// This is the only thing that wrapper needs to handle because
338343
// all access to the wrapped variable and it's projection
339344
// is routed through it.
340-
if (property->getOriginalWrappedProperty(
341-
PropertyWrapperSynthesizedPropertyKind::Backing))
342-
return true;
345+
if (auto *wrappedProperty = property->getOriginalWrappedProperty(
346+
PropertyWrapperSynthesizedPropertyKind::Backing)) {
347+
// If wrapped property is ignored - its backing storage is
348+
// ignored as well.
349+
return !wrappedProperty->getAttrs()
350+
.hasAttribute<TypeWrapperIgnoredAttr>();
351+
}
343352
}
344353

345354
// Don't wrap any compiler synthesized properties except to

test/IDE/complete_decl_attribute.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ actor MyGlobalActor {
172172
// ON_GLOBALVAR-DAG: Keyword/None: noDerivative[#Var Attribute#]; name=noDerivative
173173
// ON_GLOBALVAR-DAG: Keyword/None: exclusivity[#Var Attribute#]; name=exclusivity
174174
// ON_GLOBALVAR-DAG: Keyword/None: preconcurrency[#Var Attribute#]; name=preconcurrency
175+
// ON_GLOBALVAR-DAG: Keyword/None: typeWrapperIgnored[#Var Attribute#]; name=typeWrapperIgnored
175176
// ON_GLOBALVAR-NOT: Keyword
176177
// ON_GLOBALVAR-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct
177178
// ON_GLOBALVAR-DAG: Decl[Struct]/CurrModule/TypeRelation[Convertible]: MyPropertyWrapper[#MyPropertyWrapper#]; name=MyPropertyWrapper
@@ -209,6 +210,7 @@ struct _S {
209210
// ON_PROPERTY-DAG: Keyword/None: noDerivative[#Var Attribute#]; name=noDerivative
210211
// ON_PROPERTY-DAG: Keyword/None: exclusivity[#Var Attribute#]; name=exclusivity
211212
// ON_PROPERTY-DAG: Keyword/None: preconcurrency[#Var Attribute#]; name=preconcurrency
213+
// ON_PROPERTY-DAG: Keyword/None: typeWrapperIgnored[#Var Attribute#]; name=typeWrapperIgnored
212214
// ON_PROPERTY-NOT: Keyword
213215
// ON_PROPERTY-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct
214216
// ON_PROPERTY-DAG: Decl[Struct]/CurrModule/TypeRelation[Convertible]: MyPropertyWrapper[#MyPropertyWrapper#]; name=MyPropertyWrapper
@@ -309,6 +311,7 @@ struct _S {
309311
// ON_MEMBER_LAST-DAG: Keyword/None: exclusivity[#Declaration Attribute#]; name=exclusivity
310312
// ON_MEMBER_LAST-DAG: Keyword/None: preconcurrency[#Declaration Attribute#]; name=preconcurrency
311313
// ON_MEMBER_LAST-DAG: Keyword/None: typeWrapper[#Declaration Attribute#]; name=typeWrapper
314+
// ON_MEMBER_LAST-DAG: Keyword/None: typeWrapperIgnored[#Declaration Attribute#]; name=typeWrapperIgnored
312315
// ON_MEMBER_LAST-NOT: Keyword
313316
// ON_MEMBER_LAST-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct
314317
// ON_MEMBER_LAST-DAG: Decl[Struct]/CurrModule/TypeRelation[Convertible]: MyPropertyWrapper[#MyPropertyWrapper#]; name=MyPropertyWrapper
@@ -379,6 +382,7 @@ func dummy2() {}
379382
// KEYWORD_LAST-DAG: Keyword/None: exclusivity[#Declaration Attribute#]; name=exclusivity
380383
// KEYWORD_LAST-DAG: Keyword/None: preconcurrency[#Declaration Attribute#]; name=preconcurrency
381384
// KEYWORD_LAST-DAG: Keyword/None: typeWrapper[#Declaration Attribute#]; name=typeWrapper
385+
// KEYWORD_LAST-DAG: Keyword/None: typeWrapperIgnored[#Declaration Attribute#]; name=typeWrapperIgnored
382386
// KEYWORD_LAST-NOT: Keyword
383387
// KEYWORD_LAST-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct
384388
// KEYWORD_LAST-DAG: Decl[Struct]/CurrModule/TypeRelation[Convertible]: MyPropertyWrapper[#MyPropertyWrapper#]; name=MyPropertyWrapper

test/Interpreter/Inputs/type_wrapper_defs.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,10 @@ public class ClassWithDesignatedInit {
146146
$_storage = .init(memberwise: $Storage(a: 42, _b: PropWrapperWithoutInit(value: b)))
147147
}
148148
}
149+
150+
@Wrapper
151+
public class PersonWithIgnoredAge {
152+
public var name: String
153+
@typeWrapperIgnored public var age: Int = 0
154+
@typeWrapperIgnored @PropWrapper public var manufacturer: String?
155+
}

test/Interpreter/type_wrappers.swift

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,3 +408,44 @@ do {
408408
// CHECK: in getter
409409
// CHECK-NEXT: [42]
410410
}
411+
412+
do {
413+
var arthur = PersonWithIgnoredAge(name: "Arthur Dent", age: 30)
414+
// CHECK: Wrapper.init($Storage(name: "Arthur Dent"))
415+
416+
print(arthur.name)
417+
// CHECK: in getter
418+
// CHECK-NEXT: Arthur Dent
419+
420+
print(arthur.age)
421+
// CHECK-NOT: in getter
422+
// CHECK-NEXT: 30
423+
424+
print(arthur.manufacturer)
425+
// CHECK-NOT: in getter
426+
// CHECK-NEXT: nil
427+
428+
arthur.age = 32
429+
// CHECK-NOT: in setter
430+
431+
var marvin = PersonWithIgnoredAge(name: "Marvin The Paranoid Android", manufacturer: "Sirius Cybernetics Corporation")
432+
// CHECK: Wrapper.init($Storage(name: "Marvin The Paranoid Android"))
433+
434+
print(marvin.name)
435+
// CHECK: in getter
436+
// CHECK-NEXT: Marvin The Paranoid Android
437+
438+
print(marvin.age)
439+
// CHECK-NOT: in getter
440+
// CHECK-NEXT: 0
441+
442+
print(marvin.manufacturer)
443+
// CHECK-NOT: in getter
444+
// CHECK-NEXT: Sirius Cybernetics Corporation
445+
446+
marvin.age = 1000
447+
// CHECK-NOT: in setter
448+
449+
marvin.manufacturer = nil
450+
// CHECK-NOT: in setter
451+
}

test/type/type_wrapper.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,3 +398,42 @@ func testActors() async {
398398
person.name = "NoName" // expected-error {{actor-isolated property 'name' can not be mutated from a non-isolated context}}
399399
person.age = 0 // expected-error {{actor-isolated property 'age' can not be mutated from a non-isolated context}}
400400
}
401+
402+
func testIgnoredAttr() {
403+
@NoopWrapper
404+
struct X {
405+
@typeWrapperIgnored static var staticVar: Int = 0 // expected-error {{@typeWrapperIgnored must not be used on static properties}}
406+
407+
@typeWrapperIgnored lazy var lazyVar: Int = { // expected-error {{@typeWrapperIgnored must not be used on lazy properties}}
408+
42
409+
}()
410+
411+
@typeWrapperIgnored var x: Int // Ok
412+
413+
var y: Int {
414+
@typeWrapperIgnored get { // expected-error {{@typeWrapperIgnored may only be used on 'var' declarations}}
415+
42
416+
}
417+
418+
@typeWrapperIgnored set { // expected-error {{@typeWrapperIgnored may only be used on 'var' declarations}}
419+
}
420+
}
421+
422+
@typeWrapperIgnored var computed: Int { // expected-error {{@typeWrapperIgnored must not be used on computed properties}}
423+
get { 42 }
424+
set {}
425+
}
426+
427+
@typeWrapperIgnored let z: Int = 0 // expected-error {{@typeWrapperIgnored may only be used on 'var' declarations}}
428+
429+
func test_param(@typeWrapperIgnored x: Int) {} // expected-error {{@typeWrapperIgnored may only be used on 'var' declarations}}
430+
431+
func test_local() {
432+
@typeWrapperIgnored var x: Int = 42 // expected-error {{@typeWrapperIgnored must not be used on local properties}}
433+
// expected-warning@-1 {{variable 'x' was never used; consider replacing with '_' or removing it}}
434+
435+
@typeWrapperIgnored let y: String // expected-error {{@typeWrapperIgnored must not be used on local properties}}
436+
// expected-warning@-1 {{immutable value 'y' was never used; consider replacing with '_' or removing it}}
437+
}
438+
}
439+
}

0 commit comments

Comments
 (0)