Skip to content

[SE-0258] Improve backward compatibility of the newer property wrappers implementation #25393

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 4 commits into from
Jun 12, 2019
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
2 changes: 1 addition & 1 deletion include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ SIMPLE_DECL_ATTR(_implementationOnly, ImplementationOnly,
DECL_ATTR(_custom, Custom,
OnAnyDecl | UserInaccessible,
85)
SIMPLE_DECL_ATTR(_propertyWrapper, PropertyWrapper,
SIMPLE_DECL_ATTR(propertyWrapper, PropertyWrapper,
OnStruct | OnClass | OnEnum,
86)
SIMPLE_DECL_ATTR(_disfavoredOverload, DisfavoredOverload,
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5150,6 +5150,10 @@ class VarDecl : public AbstractStorageDecl {
/// \end
bool isPropertyWrapperInitializedWithInitialValue() const;

/// Whether the memberwise initializer parameter for a property with a property wrapper type
/// uses the wrapped type.
bool isPropertyMemberwiseInitializedWithWrappedType() const;

/// If this property is the backing storage for a property with an attached
/// property wrapper, return the original property.
///
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -4462,6 +4462,9 @@ ERROR(property_wrapper_type_not_usable_from_inline,none,
"%select{%select{variable|constant}0|property}1 "
"must be '@usableFromInline' or public",
(bool, bool))
WARNING(property_wrapper_delegateValue,none,
"property wrapper's `delegateValue` property should be renamed to "
"'wrapperValue'; use of 'delegateValue' is deprecated", ())

//------------------------------------------------------------------------------
// MARK: function builder diagnostics
Expand Down
34 changes: 27 additions & 7 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5473,10 +5473,6 @@ VarDecl *VarDecl::getPropertyWrapperBackingProperty() const {
}

bool VarDecl::isPropertyWrapperInitializedWithInitialValue() const {
auto &ctx = getASTContext();
if (!ctx.getLazyResolver())
return false;

auto customAttr = getAttachedPropertyWrapper();
if (!customAttr)
return false;
Expand All @@ -5495,9 +5491,33 @@ bool VarDecl::isPropertyWrapperInitializedWithInitialValue() const {
if (customAttr->getArg() != nullptr)
return false;

// If the property wrapper is default-initializable, it's the wrapper
// being initialized.
if (PBD->isDefaultInitializable(0))
// Default initialization does not use a value.
auto wrapperTypeInfo = getAttachedPropertyWrapperTypeInfo();
if (wrapperTypeInfo.defaultInit)
return false;

// There is no initializer, so the initialization form depends on
// whether the property wrapper type has an init(initialValue:).
return wrapperTypeInfo.initialValueInit != nullptr;
}

bool VarDecl::isPropertyMemberwiseInitializedWithWrappedType() const {
auto customAttr = getAttachedPropertyWrapper();
if (!customAttr)
return false;

auto *PBD = getParentPatternBinding();
if (!PBD)
return false;

// If there was an initializer on the original property, initialize
// via the initial value.
if (PBD->getPatternList()[0].getEqualLoc().isValid())
return true;

// If there was an initializer on the attribute itself, initialize
// via the full wrapper.
if (customAttr->getArg() != nullptr)
return false;

// There is no initializer, so the initialization form depends on
Expand Down
6 changes: 6 additions & 0 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1800,6 +1800,12 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc At
checkInvalidAttrName("_inlineable", "inlinable", DAK_Inlinable);
}

// Other names of property wrappers...
checkInvalidAttrName("propertyDelegate", "propertyWrapper",
DAK_PropertyWrapper, diag::attr_renamed_warning);
checkInvalidAttrName("_propertyWrapper", "propertyWrapper",
DAK_PropertyWrapper, diag::attr_renamed_warning);

if (DK == DAK_Count && Tok.getText() == "warn_unused_result") {
// The behavior created by @warn_unused_result is now the default. Emit a
// Fix-It to remove.
Expand Down
11 changes: 7 additions & 4 deletions lib/SILGen/SILGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1137,10 +1137,13 @@ emitStoredPropertyInitialization(PatternBindingDecl *pbd, unsigned i) {
// If this is the backing storage for a property with an attached wrapper
// that was initialized with `=`, use that expression as the initializer.
if (auto originalProperty = var->getOriginalWrappedProperty()) {
auto wrapperInfo =
originalProperty->getPropertyWrapperBackingPropertyInfo();
if (wrapperInfo.originalInitialValue)
init = wrapperInfo.originalInitialValue;
if (originalProperty
->isPropertyMemberwiseInitializedWithWrappedType()) {
auto wrapperInfo =
originalProperty->getPropertyWrapperBackingPropertyInfo();
if (wrapperInfo.originalInitialValue)
init = wrapperInfo.originalInitialValue;
}
}

SILDeclRef constant(var, SILDeclRef::Kind::StoredPropertyInitializer);
Expand Down
16 changes: 10 additions & 6 deletions lib/SILGen/SILGenConstructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ static bool maybeEmitPropertyWrapperInitFromValue(
llvm::function_ref<void(Expr *)> body) {
auto originalProperty = field->getOriginalWrappedProperty();
if (!originalProperty ||
!originalProperty->isPropertyWrapperInitializedWithInitialValue())
!originalProperty->isPropertyMemberwiseInitializedWithWrappedType())
return false;

auto wrapperInfo = originalProperty->getPropertyWrapperBackingPropertyInfo();
Expand Down Expand Up @@ -981,11 +981,15 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc,
// property wrapper initialized with `=`, inject the value into an
// instance of the wrapper.
if (auto singleVar = pbd->getSingleVar()) {
(void)maybeEmitPropertyWrapperInitFromValue(
*this, init, singleVar, std::move(result),
[&](Expr *expr) {
result = emitRValue(expr);
});
auto originalVar = singleVar->getOriginalWrappedProperty();
if (originalVar &&
originalVar->isPropertyWrapperInitializedWithInitialValue()) {
(void)maybeEmitPropertyWrapperInitFromValue(
*this, init, singleVar, std::move(result),
[&](Expr *expr) {
result = emitRValue(expr);
});
}
}

emitMemberInit(*this, selfDecl, entry.getPattern(), std::move(result));
Expand Down
27 changes: 17 additions & 10 deletions lib/Sema/CodeSynthesis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2247,12 +2247,18 @@ static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var,
if (!var->getParentPattern()->getSingleVar())
return;

// If we don't have an expression initializer or silgen can't assign a default
// initializer, then we can't generate a default value. An example of where
// silgen can assign a default is var x: Int? where the default is nil.
// If the variable is lazy, go ahead and give it a default value.
if (!var->getAttrs().hasAttribute<LazyAttr>() &&
!var->getParentPatternBinding()->isDefaultInitializable())
// Determine whether this variable will be 'nil' initialized.
bool isNilInitialized =
(isa<OptionalType>(var->getValueInterfaceType().getPointer()) &&
!var->isParentInitialized()) ||
var->getAttrs().hasAttribute<LazyAttr>();

// Whether we have explicit initialization.
bool isExplicitlyInitialized = var->isParentInitialized();

// If this is neither nil-initialized nor explicitly initialized, don't add
// anything.
if (!isNilInitialized && !isExplicitlyInitialized)
return;

// We can add a default value now.
Expand All @@ -2268,13 +2274,14 @@ static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var,
// default arg. All lazy variables return a nil literal as well. *Note* that
// the type will always be a sugared T? because we don't default init an
// explicit Optional<T>.
if ((isa<OptionalType>(var->getValueInterfaceType().getPointer()) &&
!var->isParentInitialized()) ||
var->getAttrs().hasAttribute<LazyAttr>()) {
if (isNilInitialized) {
arg->setDefaultArgumentKind(DefaultArgumentKind::NilLiteral);
return;
}

// Explicitly initialize.
assert(isExplicitlyInitialized);

// If there's a backing storage property, the memberwise initializer
// will be in terms of that.
VarDecl *backingStorageVar = var->getPropertyWrapperBackingProperty();
Expand Down Expand Up @@ -2334,7 +2341,7 @@ ConstructorDecl *swift::createImplicitConstructor(TypeChecker &tc,
// accept a value of the original property type. Otherwise, the
// memberwise initializer will be in terms of the backing storage
// type.
if (!var->isPropertyWrapperInitializedWithInitialValue()) {
if (!var->isPropertyMemberwiseInitializedWithWrappedType()) {
varInterfaceType = backingPropertyType;
}
}
Expand Down
16 changes: 14 additions & 2 deletions lib/Sema/TypeCheckPropertyWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ static ConstructorDecl *findDefaultInit(ASTContext &ctx,
llvm::Expected<PropertyWrapperTypeInfo>
PropertyWrapperTypeInfoRequest::evaluate(
Evaluator &eval, NominalTypeDecl *nominal) const {
// We must have the @_propertyWrapper attribute to continue.
// We must have the @propertyWrapper attribute to continue.
if (!nominal->getAttrs().hasAttribute<PropertyWrapperAttr>()) {
return PropertyWrapperTypeInfo();
}
Expand All @@ -245,6 +245,18 @@ PropertyWrapperTypeInfoRequest::evaluate(
result.wrapperValueVar =
findValueProperty(ctx, nominal, ctx.Id_wrapperValue, /*allowMissing=*/true);

// If there was no wrapperValue property, but there is a delegateValue
// property, use that and warn.
if (!result.wrapperValueVar) {
result.wrapperValueVar =
findValueProperty(ctx, nominal, ctx.Id_delegateValue,
/*allowMissing=*/true);
if (result.wrapperValueVar) {
result.wrapperValueVar->diagnose(diag::property_wrapper_delegateValue)
.fixItReplace(result.wrapperValueVar->getNameLoc(), "wrapperValue");
}
}

return result;
}

Expand All @@ -259,7 +271,7 @@ AttachedPropertyWrapperRequest::evaluate(Evaluator &evaluator,
auto nominal = evaluateOrDefault(
ctx.evaluator, CustomAttrNominalRequest{mutableAttr, dc}, nullptr);

// If we didn't find a nominal type with a @_propertyWrapper attribute,
// If we didn't find a nominal type with a @propertyWrapper attribute,
// skip this custom attribute.
if (!nominal || !nominal->getAttrs().hasAttribute<PropertyWrapperAttr>())
continue;
Expand Down
10 changes: 5 additions & 5 deletions test/IDE/complete_decl_attribute.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class C {}
// KEYWORD3-NEXT: Keyword/None: objcMembers[#Class Attribute#]; name=objcMembers{{$}}
// KEYWORD3-NEXT: Keyword/None: NSApplicationMain[#Class Attribute#]; name=NSApplicationMain{{$}}
// KEYWORD3-NEXT: Keyword/None: usableFromInline[#Class Attribute#]; name=usableFromInline
// KEYWORD3-NEXT: Keyword/None: _propertyWrapper[#Class Attribute#]; name=_propertyWrapper
// KEYWORD3-NEXT: Keyword/None: propertyWrapper[#Class Attribute#]; name=propertyWrapper
// KEYWORD3-NEXT: Keyword/None: _functionBuilder[#Class Attribute#]; name=_functionBuilder
// KEYWORD3-NEXT: End completions

Expand All @@ -91,7 +91,7 @@ enum E {}
// KEYWORD4-NEXT: Keyword/None: dynamicCallable[#Enum Attribute#]; name=dynamicCallable
// KEYWORD4-NEXT: Keyword/None: dynamicMemberLookup[#Enum Attribute#]; name=dynamicMemberLookup
// KEYWORD4-NEXT: Keyword/None: usableFromInline[#Enum Attribute#]; name=usableFromInline
// KEYWORD4-NEXT: Keyword/None: _propertyWrapper[#Enum Attribute#]; name=_propertyWrapper
// KEYWORD4-NEXT: Keyword/None: propertyWrapper[#Enum Attribute#]; name=propertyWrapper
// KEYWORD4-NEXT: Keyword/None: _functionBuilder[#Enum Attribute#]; name=_functionBuilder
// KEYWORD4-NEXT: End completions

Expand All @@ -103,7 +103,7 @@ struct S{}
// KEYWORD5-NEXT: Keyword/None: dynamicCallable[#Struct Attribute#]; name=dynamicCallable
// KEYWORD5-NEXT: Keyword/None: dynamicMemberLookup[#Struct Attribute#]; name=dynamicMemberLookup
// KEYWORD5-NEXT: Keyword/None: usableFromInline[#Struct Attribute#]; name=usableFromInline
// KEYWORD5-NEXT: Keyword/None: _propertyWrapper[#Struct Attribute#]; name=_propertyWrapper
// KEYWORD5-NEXT: Keyword/None: propertyWrapper[#Struct Attribute#]; name=propertyWrapper
// KEYWORD5-NEXT: Keyword/None: _functionBuilder[#Struct Attribute#]; name=_functionBuilder
// KEYWORD5-NEXT: End completions

Expand Down Expand Up @@ -204,7 +204,7 @@ struct _S {
// ON_MEMBER_LAST-DAG: Keyword/None: discardableResult[#Declaration Attribute#]; name=discardableResult
// ON_MEMBER_LAST-DAG: Keyword/None: GKInspectable[#Declaration Attribute#]; name=GKInspectable
// ON_MEMBER_LAST-DAG: Keyword/None: IBSegueAction[#Declaration Attribute#]; name=IBSegueAction
// ON_MEMBER_LAST-DAG: Keyword/None: _propertyWrapper[#Declaration Attribute#]; name=_propertyWrapper
// ON_MEMBER_LAST-DAG: Keyword/None: propertyWrapper[#Declaration Attribute#]; name=propertyWrapper
// ON_MEMBER_LAST-DAG: Keyword/None: _functionBuilder[#Declaration Attribute#]; name=_functionBuilder
// ON_MEMBER_LAST-NOT: Keyword
// ON_MEMBER_LAST: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct
Expand Down Expand Up @@ -236,7 +236,7 @@ struct _S {
// KEYWORD_LAST-NEXT: Keyword/None: usableFromInline[#Declaration Attribute#]; name=usableFromInline{{$}}
// KEYWORD_LAST-NEXT: Keyword/None: discardableResult[#Declaration Attribute#]; name=discardableResult
// KEYWORD_LAST-NEXT: Keyword/None: GKInspectable[#Declaration Attribute#]; name=GKInspectable{{$}}
// KEYWORD_LAST-NEXT: Keyword/None: _propertyWrapper[#Declaration Attribute#]; name=_propertyWrapper
// KEYWORD_LAST-NEXT: Keyword/None: propertyWrapper[#Declaration Attribute#]; name=propertyWrapper
// KEYWORD_LAST-NEXT: Keyword/None: _functionBuilder[#Declaration Attribute#]; name=_functionBuilder{{$}}
// KEYWORD_LAST-NEXT: Keyword/None: IBSegueAction[#Declaration Attribute#]; name=IBSegueAction{{$}}
// KEYWORD_LAST-NOT: Keyword
Expand Down
2 changes: 1 addition & 1 deletion test/IDE/complete_property_delegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SELF_VARNAME | %FileCheck %s -check-prefix=CONTEXT_VARNAME
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SELF_DOLLAR_VARNAME | %FileCheck %s -check-prefix=CONTEXT_DOLLAR_VARNAME

@_propertyWrapper
@propertyWrapper
struct Lazzzy<T> {
var value: T

Expand Down
2 changes: 1 addition & 1 deletion test/IDE/complete_property_delegate_attribute.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ enum MyEnum {
case east, west
}

@_propertyWrapper
@propertyWrapper
struct MyStruct {
var value: MyEnum
init(initialValue: MyEnum) {}
Expand Down
6 changes: 3 additions & 3 deletions test/IDE/print_property_wrappers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Directly printing the type-checked AST
// RUN: %target-swift-ide-test -print-ast-typechecked -source-filename %s | %FileCheck %s

@_propertyWrapper
@propertyWrapper
struct Wrapper<Value> {
var _stored: Value?

Expand Down Expand Up @@ -47,9 +47,9 @@ struct HasWrappers {
var z: String

// Memberwise initializer.
// CHECK: init(x: Wrapper<Int> = Wrapper(closure: foo), y: Bool = true, z: Wrapper<String> = Wrapper())
// CHECK: init(x: Wrapper<Int> = Wrapper(closure: foo), y: Bool = true, z: String)
}

func trigger() {
_ = HasWrappers(y: false)
_ = HasWrappers(y: false, z: "hello")
}
2 changes: 1 addition & 1 deletion test/Index/property_wrappers.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: %target-swift-ide-test -print-indexed-symbols -source-filename %s | %FileCheck -check-prefix=CHECK %s

@_propertyWrapper
@propertyWrapper
public struct Wrapper<T> {
public var value: T

Expand Down
4 changes: 2 additions & 2 deletions test/ParseableInterface/property_wrappers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
// RUN: %target-swift-frontend -build-module-from-parseable-interface -swift-version 5 %t/TestResilient.swiftinterface -o %t/TestResilient.swiftmodule
// RUN: %target-swift-frontend -emit-module -o /dev/null -merge-modules -swift-version 5 -emit-parseable-module-interface-path - %t/TestResilient.swiftmodule -module-name TestResilient | %FileCheck %s --check-prefix CHECK --check-prefix RESILIENT

@_propertyWrapper
@propertyWrapper
public struct Wrapper<T> {
public var value: T
}

@_propertyWrapper
@propertyWrapper
public struct WrapperWithInitialValue<T> {
public var value: T

Expand Down
12 changes: 6 additions & 6 deletions test/SILGen/property_wrappers.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// RUN: %target-swift-frontend -primary-file %s -emit-silgen | %FileCheck %s
// FIXME: switch to %target-swift-emit-silgen once we have syntax tree support

@_propertyWrapper
@propertyWrapper
struct Wrapper<T> {
var value: T
}

@_propertyWrapper
@propertyWrapper
struct WrapperWithInitialValue<T> {
var value: T

Expand Down Expand Up @@ -107,7 +107,7 @@ func forceHasMemberwiseInit() {
// CHECK: bb0:
// CHECK: function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF
struct HasNested<T> {
@_propertyWrapper
@propertyWrapper
private struct PrivateWrapper<U> {
var value: U
init(initialValue: U) {
Expand Down Expand Up @@ -180,7 +180,7 @@ struct WrapperWithDidSetWillSet {
}
}

@_propertyWrapper
@propertyWrapper
struct WrapperWithStorageValue<T> {
var value: T

Expand All @@ -201,7 +201,7 @@ struct UseWrapperWithStorageValue {
}
}

@_propertyWrapper
@propertyWrapper
enum Lazy<Value> {
case uninitialized(() -> Value)
case initialized(Value)
Expand Down Expand Up @@ -270,7 +270,7 @@ extension ClassUsingWrapper {
}

//
@_propertyWrapper
@propertyWrapper
struct WrapperWithDefaultInit<T> {
private var storage: T?

Expand Down
Loading