diff --git a/docs/SIL.rst b/docs/SIL.rst index 3bb31eb6714e9..893b4db6d8856 100644 --- a/docs/SIL.rst +++ b/docs/SIL.rst @@ -2333,6 +2333,32 @@ with a sequence that also correctly destroys the current value. This instruction is only valid in Raw SIL and is rewritten as appropriate by the definitive initialization pass. +assign_by_delegate +`````````````````` +:: + + sil-instruction ::= 'assign_by_delegate' sil-operand 'to' sil-operand ',' 'init' sil-operand ',' 'set' sil-operand + + assign_by_delegate %0 : $S to %1 : $*T, init %2 : $F, set %3 : $G + // $S can be a value or address type + // $T must be the type of a property delegate. + // $F must be a function type, taking $S as a single argument and returning $T + // $G must be a function type, taking $S as a single argument and with not return value + +Similar to the ``assign`` instruction, but the assignment is done via a +delegate. + +In case of an initialization, the function ``%2`` is called with ``%0`` as +argument. The result is stored to ``%1``. In case ``%2`` is an address type, +it is simply passed as a first out-argument to ``%2``. + +In case of a re-assignment, the function ``%3`` is called with ``%0`` as +argument. As ``%3`` is a setter (e.g. for the property in the containing +nominal type), the destination address ``%1`` is not used in this case. + +This instruction is only valid in Raw SIL and is rewritten as appropriate +by the definitive initialization pass. + mark_uninitialized `````````````````` :: diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 6b025d5d77804..465ed623911de 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -833,6 +833,13 @@ class ASTContext final { const IterableDeclContext *idc, LazyMemberLoader *lazyLoader); + /// Access the side cache for property delegate backing property types, + /// used because TypeChecker::typeCheckBinding() needs somewhere to stash + /// the backing property type. + Type getSideCachedPropertyDelegateBackingPropertyType(VarDecl *var) const; + void setSideCachedPropertyDelegateBackingPropertyType(VarDecl *var, + Type type); + /// Returns memory usage of this ASTContext. size_t getTotalMemory() const; diff --git a/include/swift/AST/ASTTypeIDZone.def b/include/swift/AST/ASTTypeIDZone.def index 3a19b31a5df5c..fa43f4f5adb13 100644 --- a/include/swift/AST/ASTTypeIDZone.def +++ b/include/swift/AST/ASTTypeIDZone.def @@ -16,4 +16,7 @@ //===----------------------------------------------------------------------===// SWIFT_TYPEID_NAMED(NominalTypeDecl *, NominalTypeDecl) SWIFT_TYPEID_NAMED(VarDecl *, VarDecl) -SWIFT_TYPEID(PropertyBehaviorTypeInfo) +SWIFT_TYPEID(Type) +SWIFT_TYPEID(PropertyDelegateBackingPropertyInfo) +SWIFT_TYPEID(PropertyDelegateTypeInfo) +SWIFT_TYPEID_NAMED(CustomAttr *, CustomAttr) diff --git a/include/swift/AST/ASTTypeIDs.h b/include/swift/AST/ASTTypeIDs.h index e13342c72b3f5..6a5b007b46862 100644 --- a/include/swift/AST/ASTTypeIDs.h +++ b/include/swift/AST/ASTTypeIDs.h @@ -20,8 +20,11 @@ #include "swift/Basic/TypeID.h" namespace swift { +class CustomAttr; class NominalTypeDecl; -struct PropertyBehaviorTypeInfo; +struct PropertyDelegateBackingPropertyInfo; +struct PropertyDelegateTypeInfo; +class Type; class VarDecl; #define SWIFT_AST_TYPEID_ZONE 1 diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index 6198a6243ac33..ff1352c11e1c9 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -399,6 +399,9 @@ SIMPLE_DECL_ATTR(_implementationOnly, ImplementationOnly, DECL_ATTR(_custom, Custom, OnAnyDecl | UserInaccessible, 85) +SIMPLE_DECL_ATTR(_propertyDelegate, PropertyDelegate, + OnStruct | OnClass | OnEnum, + 86) #undef TYPE_ATTR #undef DECL_ATTR_ALIAS diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 1605bd089f13e..bf30085d5d194 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -1428,6 +1428,7 @@ class CustomAttr final : public DeclAttribute, TypeLoc type; Expr *arg; PatternBindingInitializer *initContext; + Expr *semanticInit = nullptr; unsigned hasArgLabelLocs : 1; unsigned numArgLabels : 16; @@ -1463,6 +1464,9 @@ class CustomAttr final : public DeclAttribute, Expr *getArg() const { return arg; } void setArg(Expr *newArg) { arg = newArg; } + Expr *getSemanticInit() const { return semanticInit; } + void setSemanticInit(Expr *expr) { semanticInit = expr; } + PatternBindingInitializer *getInitContext() const { return initContext; } static bool classof(const DeclAttribute *DA) { diff --git a/include/swift/AST/CaptureInfo.h b/include/swift/AST/CaptureInfo.h index c1cab207f0b37..b5f14d1fa896e 100644 --- a/include/swift/AST/CaptureInfo.h +++ b/include/swift/AST/CaptureInfo.h @@ -17,6 +17,7 @@ #include "swift/AST/TypeAlignments.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/PointerIntPair.h" +#include "llvm/ADT/PointerUnion.h" #include namespace swift { @@ -31,13 +32,20 @@ template <> struct DenseMapInfo; namespace swift { class ValueDecl; class FuncDecl; +class OpaqueValueExpr; /// CapturedValue includes both the declaration being captured, along with flags /// that indicate how it is captured. class CapturedValue { - llvm::PointerIntPair Value; +public: + using Storage = + llvm::PointerIntPair, 2, + unsigned>; + +private: + Storage Value; - explicit CapturedValue(llvm::PointerIntPair V) : Value(V) {} + explicit CapturedValue(Storage V) : Value(V) {} public: friend struct llvm::DenseMapInfo; @@ -53,16 +61,21 @@ class CapturedValue { IsNoEscape = 1 << 1 }; - CapturedValue(ValueDecl *D, unsigned Flags) : Value(D, Flags) {} + CapturedValue(llvm::PointerUnion Ptr, + unsigned Flags) + : Value(Ptr, Flags) {} static CapturedValue getDynamicSelfMetadata() { - return CapturedValue(nullptr, 0); + return CapturedValue((ValueDecl *)nullptr, 0); } bool isDirect() const { return Value.getInt() & IsDirect; } bool isNoEscape() const { return Value.getInt() & IsNoEscape; } bool isDynamicSelfMetadata() const { return !Value.getPointer(); } + bool isOpaqueValue() const { + return Value.getPointer().is(); + } CapturedValue mergeFlags(CapturedValue cv) { assert(Value.getPointer() == cv.Value.getPointer() && @@ -73,7 +86,13 @@ class CapturedValue { ValueDecl *getDecl() const { assert(Value.getPointer() && "dynamic Self metadata capture does not " "have a value"); - return Value.getPointer(); + return Value.getPointer().dyn_cast(); + } + + OpaqueValueExpr *getOpaqueValue() const { + assert(Value.getPointer() && "dynamic Self metadata capture does not " + "have a value"); + return Value.getPointer().dyn_cast(); } unsigned getFlags() const { return Value.getInt(); } @@ -98,8 +117,7 @@ namespace llvm { template <> struct DenseMapInfo { using CapturedValue = swift::CapturedValue; - using PtrIntPairDenseMapInfo = - DenseMapInfo>; + using PtrIntPairDenseMapInfo = DenseMapInfo; static inline swift::CapturedValue getEmptyKey() { return CapturedValue{PtrIntPairDenseMapInfo::getEmptyKey()}; @@ -128,19 +146,20 @@ class DynamicSelfType; class CaptureInfo { const CapturedValue *Captures; DynamicSelfType *DynamicSelf; + OpaqueValueExpr *OpaqueValue; unsigned Count = 0; bool GenericParamCaptures : 1; bool Computed : 1; public: CaptureInfo() - : Captures(nullptr), DynamicSelf(nullptr), Count(0), + : Captures(nullptr), DynamicSelf(nullptr), OpaqueValue(nullptr), Count(0), GenericParamCaptures(0), Computed(0) { } bool hasBeenComputed() { return Computed; } bool isTrivial() { - return Count == 0 && !GenericParamCaptures && !DynamicSelf; + return Count == 0 && !GenericParamCaptures && !DynamicSelf && !OpaqueValue; } ArrayRef getCaptures() const { @@ -184,6 +203,18 @@ class CaptureInfo { DynamicSelf = dynamicSelf; } + bool hasOpaqueValueCapture() const { + return OpaqueValue != nullptr; + } + + OpaqueValueExpr *getOpaqueValue() const { + return OpaqueValue; + } + + void setOpaqueValue(OpaqueValueExpr *OVE) { + OpaqueValue = OVE; + } + void dump() const; void print(raw_ostream &OS) const; }; diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 2b4d5ebaa0ba9..10a33587fb1fc 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -78,6 +78,8 @@ namespace swift { class ParameterTypeFlags; class Pattern; struct PrintOptions; + struct PropertyDelegateBackingPropertyInfo; + struct PropertyDelegateTypeInfo; class ProtocolDecl; class ProtocolType; struct RawComment; @@ -364,7 +366,7 @@ class alignas(1 << DeclAlignInBits) Decl { IsStatic : 1 ); - SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 4+1+1+1+1, + SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 4+1+1+1+1+1, /// The specifier associated with this variable or parameter. This /// determines the storage semantics of the value e.g. mutability. Specifier : 4, @@ -383,7 +385,10 @@ class alignas(1 << DeclAlignInBits) Decl { /// Whether this is a property defined in the debugger's REPL. /// FIXME: Remove this once LLDB has proper support for resilience. - IsREPLVar : 1 + IsREPLVar : 1, + + /// Whether this is the backing storage for a property delegate. + IsPropertyDelegateBackingProperty : 1 ); SWIFT_INLINE_BITFIELD(ParamDecl, VarDecl, 1 + NumDefaultArgumentKindBits, @@ -1886,7 +1891,10 @@ class PatternBindingEntry { enum class Flags { Checked = 1 << 0, Removed = 1 << 1, - Lazy = 1 << 2 + /// Whether the contents of this initializer were subsumed by + /// some other initialization, e.g., a lazy property's initializer + /// gets subsumed by the getter body. + Subsumed = 1 << 2 }; llvm::PointerIntPair> PatternAndFlags; @@ -1921,14 +1929,20 @@ class PatternBindingEntry { Pattern *getPattern() const { return PatternAndFlags.getPointer(); } void setPattern(Pattern *P) { PatternAndFlags.setPointer(P); } + + /// Whether the given pattern binding entry is initialized. + bool isInitialized() const; + Expr *getInit() const { if (PatternAndFlags.getInt().contains(Flags::Removed) || InitContextAndIsText.getInt()) return nullptr; return InitExpr.Node; } - Expr *getNonLazyInit() const { - return isInitializerLazy() ? nullptr : getInit(); + /// Retrieve the initializer if it should be executed to initialize this + /// particular pattern binding. + Expr *getExecutableInit() const { + return isInitializerSubsumed() ? nullptr : getInit(); } SourceRange getOrigInitRange() const; void setInit(Expr *E); @@ -1972,11 +1986,11 @@ class PatternBindingEntry { PatternAndFlags.setInt(PatternAndFlags.getInt() | Flags::Checked); } - bool isInitializerLazy() const { - return PatternAndFlags.getInt().contains(Flags::Lazy); + bool isInitializerSubsumed() const { + return PatternAndFlags.getInt().contains(Flags::Subsumed); } - void setInitializerLazy() { - PatternAndFlags.setInt(PatternAndFlags.getInt() | Flags::Lazy); + void setInitializerSubsumed() { + PatternAndFlags.setInt(PatternAndFlags.getInt() | Flags::Subsumed); } // Return the first variable initialized by this pattern. @@ -2068,11 +2082,16 @@ class PatternBindingDecl final : public Decl, getMutablePatternList()[i].setInitStringRepresentation(str); } + /// Whether the given pattern entry is initialized. + bool isInitialized(unsigned i) const { + return getPatternList()[i].isInitialized(); + } + Expr *getInit(unsigned i) const { return getPatternList()[i].getInit(); } - Expr *getNonLazyInit(unsigned i) const { - return getPatternList()[i].getNonLazyInit(); + Expr *getExecutableInit(unsigned i) const { + return getPatternList()[i].getExecutableInit(); } SourceRange getOrigInitRange(unsigned i) const { @@ -2113,12 +2132,12 @@ class PatternBindingDecl final : public Decl, getMutablePatternList()[i].setInitializerChecked(); } - bool isInitializerLazy(unsigned i) const { - return getPatternList()[i].isInitializerLazy(); + bool isInitializerSubsumed(unsigned i) const { + return getPatternList()[i].isInitializerSubsumed(); } - void setInitializerLazy(unsigned i) { - getMutablePatternList()[i].setInitializerLazy(); + void setInitializerSubsumed(unsigned i) { + getMutablePatternList()[i].setInitializerSubsumed(); } /// Does this binding declare something that requires storage? @@ -2126,11 +2145,7 @@ class PatternBindingDecl final : public Decl, /// Determines whether this binding either has an initializer expression, or is /// default initialized, without performing any type checking on it. - /// - /// This is only valid to check for bindings which have storage. bool isDefaultInitializable() const { - assert(hasStorage()); - for (unsigned i = 0, e = getNumPatternEntries(); i < e; ++i) if (!isDefaultInitializable(i)) return false; @@ -3358,6 +3373,9 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { /// Is this a key path type? Optional getKeyPathTypeKind() const; + /// Retrieve information about this type as a property delegate. + PropertyDelegateTypeInfo getPropertyDelegateTypeInfo() const; + private: /// Predicate used to filter StoredPropertyRange. struct ToStoredProperty { @@ -4700,6 +4718,17 @@ class AbstractStorageDecl : public ValueDecl { } }; +/// Describes which synthesized property for a property with an attached +/// delegate is being referenced. +enum class PropertyDelegateSynthesizedPropertyKind { + /// The backing storage property, which is a stored property of the + /// delegate type. + Backing, + /// A storage delegate (e.g., `$foo`), which is a wrapper over the + /// delegate instance's `delegateValue` property. + StorageDelegate, +}; + /// VarDecl - 'var' and 'let' declarations. class VarDecl : public AbstractStorageDecl { public: @@ -4730,6 +4759,7 @@ class VarDecl : public AbstractStorageDecl { Bits.VarDecl.IsDebuggerVar = false; Bits.VarDecl.IsREPLVar = false; Bits.VarDecl.HasNonPatternBindingInit = false; + Bits.VarDecl.IsPropertyDelegateBackingProperty = false; } /// This is the type specified, including location information. @@ -4896,12 +4926,19 @@ class VarDecl : public AbstractStorageDecl { return nullptr; } + /// Whether there exists an initializer for this \c VarDecl. + bool isParentInitialized() const { + if (auto *PBD = getParentPatternBinding()) + return PBD->getPatternEntryForVarDecl(this).isInitialized(); + return false; + } + // Return whether this VarDecl has an initial value, either by checking // if it has an initializer in its parent pattern binding or if it has // the @_hasInitialValue attribute. bool hasInitialValue() const { return getAttrs().hasAttribute() || - getParentInitializer(); + isParentInitialized(); } VarDecl *getOverriddenDecl() const { @@ -5017,6 +5054,65 @@ class VarDecl : public AbstractStorageDecl { Bits.VarDecl.IsREPLVar = IsREPLVar; } + /// Retrieve the custom attribute that attaches a property delegate to this + /// property. + CustomAttr *getAttachedPropertyDelegate() const; + + /// Retrieve the type of the attached property delegate as a contextual + /// type. + /// + /// \returns a NULL type for properties without attached delegates, + /// an error type when the property delegate type itself is erroneous, + /// or the delegate type itself, which may involve unbound generic + /// types. + Type getAttachedPropertyDelegateType() const; + + /// Retrieve information about the attached property delegate type. + PropertyDelegateTypeInfo getAttachedPropertyDelegateTypeInfo() const; + + /// Retrieve the fully resolved attached property delegate type. + /// + /// This type will be the fully-resolved form of + /// \c getAttachedPropertyDelegateType(), which will not contain any + /// unbound generic types. It will be the type of the backing property. + Type getPropertyDelegateBackingPropertyType() const; + + /// Retrieve information about the backing properties of the attached + /// property delegate. + PropertyDelegateBackingPropertyInfo + getPropertyDelegateBackingPropertyInfo() const; + + /// Retrieve the backing storage property for a property that has an + /// attached property delegate. + /// + /// The backing storage property will be a stored property of the + /// delegate's type. This will be equivalent to + /// \c getAttachedPropertyDelegateType() when it is fully-specified; + /// if \c getAttachedPropertyDelegateType() involves an unbound + /// generic type, the backing storage property will be the appropriate + /// bound generic version. + VarDecl *getPropertyDelegateBackingProperty() const; + + /// Whether this is a property with a property delegate that was initialized + /// via a value of the original type, e.g., + /// + /// \code + /// @Lazy var i = 17 + /// \end + bool isPropertyDelegateInitializedWithInitialValue() const; + + /// If this property is the backing storage for a property with an attached + /// property delegate, return the original property. + /// + /// \param kind If not \c None, only returns the original property when + /// \c this property is the specified synthesized property. + VarDecl *getOriginalDelegatedProperty( + Optional kind = None) const; + + /// Set the property that delegates to this property as it's backing + /// property. + void setOriginalDelegatedProperty(VarDecl *originalProperty); + /// Return the Objective-C runtime name for this property. Identifier getObjCPropertyName() const; diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 172c709dbc145..1d23bb3f5806f 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1099,14 +1099,14 @@ ERROR(invalid_postfix_operator,none, ERROR(expected_member_name,PointsToFirstBadToken, "expected member name following '.'", ()) -ERROR(expected_dollar_numeric,none, - "expected numeric value following '$'", ()) ERROR(dollar_numeric_too_large,none, "numeric value following '$' is too large", ()) ERROR(numeric_literal_numeric_member,none, "expected named member of numeric literal", ()) ERROR(standalone_dollar_identifier,none, "'$' is not an identifier; use backticks to escape it", ()) +ERROR(dollar_identifier_decl,none, + "cannot declare entity %0 with a '$' prefix", (Identifier)) ERROR(anon_closure_arg_not_in_closure,none, "anonymous closure argument not contained in a closure", ()) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 80c343b72cbb2..3ce7c05e473cd 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1074,7 +1074,7 @@ ERROR(c_function_pointer_from_generic_function,none, ERROR(c_function_pointer_from_function_with_context,none, "a C function pointer cannot be formed from a " "%select{local function|closure}0 that captures " - "%select{context|generic parameters|dynamic Self type}1", + "%select{context|generic parameters|dynamic Self type|<}1", (bool, unsigned)) ERROR(invalid_autoclosure_forwarding,none, "add () to forward @autoclosure parameter", ()) @@ -4312,6 +4312,88 @@ WARNING(hashvalue_implementation,none, "conform type %0 to 'Hashable' by implementing 'hash(into:)' instead", (Type)) +//------------------------------------------------------------------------------ +// MARK: property delegate diagnostics +//------------------------------------------------------------------------------ +ERROR(property_delegate_no_value_property, none, + "property delegate type %0 does not contain a non-static property " + "named %1", (Type, Identifier)) +ERROR(property_delegate_ambiguous_value_property, none, + "property delegate type %0 has multiple non-static properties " + "named %1", (Type, Identifier)) +ERROR(property_delegate_wrong_initial_value_init, none, + "'init(initialValue:)' parameter type (%0) must be the same as its " + "'value' property type (%1) or an @autoclosure thereof", (Type, Type)) +ERROR(property_delegate_failable_initial_value_init, none, + "'init(initialValue:)' cannot be failable", ()) +ERROR(property_delegate_ambiguous_initial_value_init, none, + "property delegate type %0 has multiple initial-value initializers", (Type)) +ERROR(property_delegate_type_requirement_not_accessible,none, + "%select{private|fileprivate|internal|public|open}0 %1 %2 cannot have " + "more restrictive access than its enclosing property delegate type %3 " + "(which is %select{private|fileprivate|internal|public|open}4)", + (AccessLevel, DescriptiveDeclKind, DeclName, Type, AccessLevel)) +ERROR(property_delegate_reserved_name,none, + "property delegate type name must start with an uppercase letter", ()) + +ERROR(property_delegate_attribute_not_on_property, none, + "property delegate attribute %0 can only be applied to a property", + (DeclName)) + +ERROR(property_delegate_multiple,none, + "only one property delegate can be attached to a given property", ()) +NOTE(previous_property_delegate_here,none, + "previous property delegate specified here", ()) + +ERROR(property_delegate_local,none, + "property delegates are not yet supported on local properties", ()) +ERROR(property_delegate_let, none, + "property delegate can only be applied to a 'var'", + ()) + +ERROR(property_with_delegate_conflict_attribute,none, + "property %0 with a delegate cannot also be " + "%select{lazy|@NSCopying|@NSManaged|weak|unowned|unmanaged}1", + (DeclName, int)) +ERROR(property_delegate_not_single_var, none, + "property delegate can only apply to a single variable", ()) +ERROR(property_with_delegate_in_bad_context,none, + "%select{|non-static |non-static }1property %0 declared inside " + "%select{a protocol|an extension|an enum}1 cannot have a delegate", + (DeclName, int)) +ERROR(property_with_delegate_overrides,none, + "property %0 with attached delegate cannot override another property", + (DeclName)) + +ERROR(property_delegate_and_normal_init,none, + "property %0 with attached delegate cannot initialize both the " + "delegate type and the property", (DeclName)) +ERROR(property_delegate_init_without_initial_value, none, + "initializing property %0 with delegate %1 that lacks " + "an 'init(initialValue:)' initializer", (DeclName, Type)) +NOTE(property_delegate_direct_init,none, + "initialize the property delegate type directly with " + "'(...') on the attribute", ()) + +ERROR(property_delegate_incompatible_unbound, none, + "property delegate type %0 must either specify all generic arguments " + "or require only a single generic argument", (Type)) + +ERROR(property_delegate_type_access,none, + "%select{%select{variable|constant}0|property}1 " + "%select{must be declared %select{" + "%select{private|fileprivate|internal|%error|%error}3|private or fileprivate}4" + "|cannot be declared " + "%select{in this context|fileprivate|internal|public|open}3}2 " + "because its property delegate type uses " + "%select{a private|a fileprivate|an internal|%error|%error}5 type", + (bool, bool, bool, AccessLevel, bool, AccessLevel)) +ERROR(property_delegate_type_not_usable_from_inline,none, + "property delegate type referenced from a '@usableFromInline' " + "%select{%select{variable|constant}0|property}1 " + "must be '@usableFromInline' or public", + (bool, bool)) + #ifndef DIAG_NO_UNDEF # if defined(DIAG) # undef DIAG diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index d690da7f4094d..04691dc5575f4 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -364,6 +364,10 @@ class alignas(8) Expr { NumElements : 32 ); + SWIFT_INLINE_BITFIELD(OpaqueValueExpr, Expr, 1, + IsPlaceholder : 1 + ); + } Bits; private: @@ -3778,8 +3782,15 @@ class OpaqueValueExpr : public Expr { SourceLoc Loc; public: - explicit OpaqueValueExpr(SourceLoc Loc, Type Ty) - : Expr(ExprKind::OpaqueValue, /*Implicit=*/true, Ty), Loc(Loc) { } + explicit OpaqueValueExpr(SourceLoc Loc, Type Ty, bool isPlaceholder = false) + : Expr(ExprKind::OpaqueValue, /*Implicit=*/true, Ty), Loc(Loc) { + Bits.OpaqueValueExpr.IsPlaceholder = isPlaceholder; + } + + /// Whether this opaque value expression represents a placeholder that + /// is injected before type checking to act as a placeholder for some + /// value to be specified later. + bool isPlaceholder() const { return Bits.OpaqueValueExpr.IsPlaceholder; } SourceRange getSourceRange() const { return Loc; } diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index fc0a792f8a3ad..01ed9b788c862 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -47,6 +47,7 @@ IDENTIFIER(decode) IDENTIFIER(decodeIfPresent) IDENTIFIER(Decoder) IDENTIFIER(decoder) +IDENTIFIER(delegateValue) IDENTIFIER(dynamicallyCall) IDENTIFIER(dynamicMember) IDENTIFIER(Element) diff --git a/include/swift/AST/PropertyDelegates.h b/include/swift/AST/PropertyDelegates.h new file mode 100644 index 0000000000000..6fc018e84642f --- /dev/null +++ b/include/swift/AST/PropertyDelegates.h @@ -0,0 +1,135 @@ +//===--- PropertyDelegates.h - Property Delegate ASTs -----------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines helper types for property delegates. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_AST_PROPERTY_DELEGATES_H +#define SWIFT_AST_PROPERTY_DELEGATES_H + +namespace llvm { + class raw_ostream; +} + +namespace swift { + +class ConstructorDecl; +class CustomAttr; +class Expr; +class VarDecl; +class OpaqueValueExpr; + +/// Describes a property delegate type. +struct PropertyDelegateTypeInfo { + /// The property through to which access that uses this delegate type is + /// delegated. + VarDecl *valueVar = nullptr; + + /// The initializer init(initialValue:) that will be called when the + /// initiqlizing the property delegate type from a value of the property type. + /// + /// This initializer is optional, but if present will be used for the `=` + /// initialization syntax. + ConstructorDecl *initialValueInit = nullptr; + + /// The property through which the delegate value ($foo) will be accessed, + /// hiding the underlying storage completely. + /// + /// This property is optional. If present, a computed property for `$foo` + /// will be created that redirects to this property. + VarDecl *delegateValueVar = nullptr; + + /// Whether this is a valid property delegate. + bool isValid() const { + return valueVar != nullptr; + } + + explicit operator bool() const { return isValid(); } + + friend bool operator==(const PropertyDelegateTypeInfo &lhs, + const PropertyDelegateTypeInfo &rhs) { + return lhs.valueVar == rhs.valueVar && + lhs.initialValueInit == rhs.initialValueInit; + } +}; + +/// Describes the backing property of a property that has an attached delegate. +struct PropertyDelegateBackingPropertyInfo { + /// The backing property. + VarDecl *backingVar = nullptr; + + /// The storage delegate property, if any. When present, this takes the name + /// '$foo' from `backingVar`. + VarDecl *storageDelegateVar = nullptr; + + /// When the original default value is specified in terms of an '=' + /// initializer on the initial property, e.g., + /// + /// \code + /// @Lazy var i = 17 + /// \end + /// + /// This is the specified initial value (\c 17), which is suitable for + /// embedding in the expression \c initializeFromOriginal. + Expr *originalInitialValue = nullptr; + + /// An expression that initializes the backing property from a value of + /// the original property's type (e.g., via `init(initialValue:)`), or + /// \c NULL if the backing property can only be initialized directly. + Expr *initializeFromOriginal = nullptr; + + /// When \c initializeFromOriginal is non-NULL, the opaque value that + /// is used as a stand-in for a value of the original property's type. + OpaqueValueExpr *underlyingValue = nullptr; + + PropertyDelegateBackingPropertyInfo() { } + + PropertyDelegateBackingPropertyInfo(VarDecl *backingVar, + VarDecl *storageDelegateVar, + Expr *originalInitialValue, + Expr *initializeFromOriginal, + OpaqueValueExpr *underlyingValue) + : backingVar(backingVar), storageDelegateVar(storageDelegateVar), + originalInitialValue(originalInitialValue), + initializeFromOriginal(initializeFromOriginal), + underlyingValue(underlyingValue) { } + + /// Whether this is a valid property delegate. + bool isValid() const { + return backingVar != nullptr; + } + + explicit operator bool() const { return isValid(); } + + friend bool operator==(const PropertyDelegateBackingPropertyInfo &lhs, + const PropertyDelegateBackingPropertyInfo &rhs) { + // FIXME: Can't currently compare expressions. + return lhs.backingVar == rhs.backingVar; + } +}; + +void simple_display( + llvm::raw_ostream &out, + const PropertyDelegateTypeInfo &propertyDelegate); + +void simple_display( + llvm::raw_ostream &out, + const PropertyDelegateBackingPropertyInfo &backingInfo); + +/// Given the initializer for the given property with an attached property +/// delegate, dig out the original initialization expression. +Expr *findOriginalPropertyDelegateInitialValue(VarDecl *var, Expr *init); + +} // end namespace swift + +#endif // SWIFT_AST_PROPERTY_DELEGATES_H diff --git a/include/swift/AST/TypeAlignments.h b/include/swift/AST/TypeAlignments.h index 4d58eda2edde3..149b8ae24598d 100644 --- a/include/swift/AST/TypeAlignments.h +++ b/include/swift/AST/TypeAlignments.h @@ -39,6 +39,7 @@ namespace swift { class GenericTypeParamDecl; class NominalTypeDecl; class NormalProtocolConformance; + class OpaqueValueExpr; class OperatorDecl; class Pattern; class ProtocolDecl; @@ -105,6 +106,7 @@ LLVM_DECLARE_TYPE_ALIGNMENT(swift::ASTContext, 2); LLVM_DECLARE_TYPE_ALIGNMENT(swift::DeclContext, swift::DeclContextAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::Expr, swift::ExprAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::AbstractClosureExpr, swift::ExprAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::OpaqueValueExpr, swift::ExprAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::ProtocolConformance, swift::DeclAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::NormalProtocolConformance, swift::DeclAlignInBits) diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 0f4af863cadc3..79c6ecb6298f1 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -16,10 +16,12 @@ #ifndef SWIFT_TYPE_CHECK_REQUESTS_H #define SWIFT_TYPE_CHECK_REQUESTS_H +#include "swift/AST/ASTTypeIDs.h" #include "swift/AST/Type.h" #include "swift/AST/Evaluator.h" #include "swift/AST/SimpleRequest.h" #include "swift/AST/TypeResolutionStage.h" +#include "swift/Basic/AnyValue.h" #include "swift/Basic/Statistic.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/STLExtras.h" @@ -28,6 +30,7 @@ namespace swift { class GenericParamList; +struct PropertyDelegateBackingPropertyInfo; class RequirementRepr; class SpecializeAttr; struct TypeLoc; @@ -415,6 +418,142 @@ class DefaultTypeRequest Type &getCache() const; }; +/// Retrieve information about a property delegate type. +class PropertyDelegateTypeInfoRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected + evaluate(Evaluator &eval, NominalTypeDecl *nominal) const; + +public: + // Caching + bool isCached() const; + + // Cycle handling + void diagnoseCycle(DiagnosticEngine &diags) const; + void noteCycleStep(DiagnosticEngine &diags) const; +}; + +/// Request the nominal type declaration to which the given custom attribute +/// refers. +class AttachedPropertyDelegateRequest : + public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected + evaluate(Evaluator &evaluator, VarDecl *) const; + +public: + // Caching + bool isCached() const; + + // Cycle handling + void diagnoseCycle(DiagnosticEngine &diags) const; + void noteCycleStep(DiagnosticEngine &diags) const; +}; + +/// Request the raw (possibly unbound generic) type of the property delegate +/// that is attached to the given variable. +class AttachedPropertyDelegateTypeRequest : + public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected + evaluate(Evaluator &evaluator, VarDecl *var) const; + +public: + // Caching + bool isCached() const; + + // Cycle handling + void diagnoseCycle(DiagnosticEngine &diags) const; + void noteCycleStep(DiagnosticEngine &diags) const; +}; + +/// Request the nominal type declaration to which the given custom attribute +/// refers. +class PropertyDelegateBackingPropertyTypeRequest : + public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected + evaluate(Evaluator &evaluator, VarDecl *var) const; + +public: + // Caching + bool isCached() const; + + // Cycle handling + void diagnoseCycle(DiagnosticEngine &diags) const; + void noteCycleStep(DiagnosticEngine &diags) const; +}; + +/// Request information about the backing property for properties that have +/// attached property delegates. +class PropertyDelegateBackingPropertyInfoRequest : + public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected + evaluate(Evaluator &evaluator, VarDecl *var) const; + +public: + // Caching + bool isCached() const; + + // Cycle handling + void diagnoseCycle(DiagnosticEngine &diags) const; + void noteCycleStep(DiagnosticEngine &diags) const; +}; + +// Allow AnyValue to compare two Type values, even though Type doesn't +// support ==. +template<> +bool AnyValue::Holder::equals(const HolderBase &other) const; + +void simple_display(llvm::raw_ostream &out, const Type &type); + /// The zone number for the type checker. #define SWIFT_TYPE_CHECKER_REQUESTS_TYPEID_ZONE 10 diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index 3067be876d875..87e92c331e848 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -25,3 +25,8 @@ SWIFT_TYPEID(RequirementRequest) SWIFT_TYPEID(USRGenerationRequest) SWIFT_TYPEID(DefaultTypeRequest) SWIFT_TYPEID(MangleLocalTypeDeclRequest) +SWIFT_TYPEID(PropertyDelegateTypeInfoRequest) +SWIFT_TYPEID(AttachedPropertyDelegateRequest) +SWIFT_TYPEID(AttachedPropertyDelegateTypeRequest) +SWIFT_TYPEID(PropertyDelegateBackingPropertyTypeRequest) +SWIFT_TYPEID(PropertyDelegateBackingPropertyInfoRequest) diff --git a/include/swift/Parse/Lexer.h b/include/swift/Parse/Lexer.h index 4d2dd718c310e..ffb8674c06e1b 100644 --- a/include/swift/Parse/Lexer.h +++ b/include/swift/Parse/Lexer.h @@ -189,6 +189,11 @@ class Lexer { return CodeCompletionPtr != nullptr; } + /// Whether we are lexing a Swift interface file. + bool isSwiftInterface() const { + return LexMode == LexerMode::SwiftInterface; + } + /// Lex a token. If \c TriviaRetentionMode is \c WithTrivia, passed pointers /// to trivias are populated. void lex(Token &Result, ParsedTrivia &LeadingTriviaResult, diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 0892d39ca6282..3b28c296cc330 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -519,10 +519,15 @@ class Parser { return consumeToken(); } - SourceLoc consumeIdentifier(Identifier *Result = nullptr) { + SourceLoc consumeIdentifier(Identifier *Result = nullptr, + bool allowDollarIdentifier = false) { assert(Tok.isAny(tok::identifier, tok::kw_self, tok::kw_Self)); if (Result) *Result = Context.getIdentifier(Tok.getText()); + + if (Tok.getText()[0] == '$' && !allowDollarIdentifier) + diagnoseDollarIdentifier(Tok); + return consumeToken(); } @@ -532,10 +537,27 @@ class Parser { if (!Tok.is(tok::kw__)) { Tok.setKind(tok::identifier); Result = Context.getIdentifier(Tok.getText()); + + if (Tok.getText()[0] == '$') + diagnoseDollarIdentifier(Tok); } return consumeToken(); } + /// When we have a token that is an identifier starting with '$', + /// diagnose it if not permitted in this mode. + void diagnoseDollarIdentifier(const Token &tok) { + assert(tok.getText()[0] == '$'); + + if (tok.getText().size() == 1 || + Context.LangOpts.EnableDollarIdentifiers || + isInSILMode() || L->isSwiftInterface()) + return; + + diagnose(tok.getLoc(), diag::dollar_identifier_decl, + Context.getIdentifier(tok.getText())); + } + /// Retrieve the location just past the end of the previous /// source location. SourceLoc getEndOfPreviousLoc(); diff --git a/include/swift/SIL/InstructionUtils.h b/include/swift/SIL/InstructionUtils.h index 88dd842db2973..cb307bdc63f8f 100644 --- a/include/swift/SIL/InstructionUtils.h +++ b/include/swift/SIL/InstructionUtils.h @@ -134,6 +134,10 @@ bool isSanitizerInstrumentation(SILInstruction *Instruction); /// argument of the partial apply if it is. SILValue isPartialApplyOfReabstractionThunk(PartialApplyInst *PAI); +/// Returns true if \p PAI is only used by an assign_by_delegate instruction as +/// init or set function. +bool onlyUsedByAssignByDelegate(PartialApplyInst *PAI); + /// If V is a function closure, return the reaching set of partial_apply's. void findClosuresForFunctionValue(SILValue V, TinyPtrVector &results); diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index a2790eabd60b6..a8448a8dada52 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -842,6 +842,16 @@ class SILBuilder { Qualifier)); } + AssignByDelegateInst *createAssignByDelegate(SILLocation Loc, + SILValue Src, SILValue Dest, + SILValue Initializer, + SILValue Setter, + AssignOwnershipQualifier Qualifier) { + return insert(new (getModule()) + AssignByDelegateInst(getSILDebugLocation(Loc), Src, Dest, + Initializer, Setter, Qualifier)); + } + StoreBorrowInst *createStoreBorrow(SILLocation Loc, SILValue Src, SILValue DestAddr) { return insert(new (getModule()) diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 6c6eafa76d969..2d61ebe4e661b 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -1204,6 +1204,18 @@ void SILCloner::visitAssignInst(AssignInst *Inst) { Inst->getOwnershipQualifier())); } +template +void SILCloner::visitAssignByDelegateInst(AssignByDelegateInst *Inst) { + getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); + recordClonedInstruction( + Inst, getBuilder().createAssignByDelegate(getOpLocation(Inst->getLoc()), + getOpValue(Inst->getSrc()), + getOpValue(Inst->getDest()), + getOpValue(Inst->getInitializer()), + getOpValue(Inst->getSetter()), + Inst->getOwnershipQualifier())); +} + template void SILCloner::visitMarkUninitializedInst(MarkUninitializedInst *Inst) { diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 5b4c7cee24023..f417d491f6520 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -3684,19 +3684,17 @@ enum class AssignOwnershipQualifier { }; static_assert(2 == SILNode::NumAssignOwnershipQualifierBits, "Size mismatch"); -/// AssignInst - Represents an abstract assignment to a memory location, which -/// may either be an initialization or a store sequence. This is only valid in -/// Raw SIL. -class AssignInst - : public InstructionBase { - friend SILBuilder; +template +class AssignInstBase + : public InstructionBase { - FixedOperandList<2> Operands; +protected: + FixedOperandList Operands; - AssignInst(SILDebugLocation DebugLoc, SILValue Src, SILValue Dest, - AssignOwnershipQualifier Qualifier = - AssignOwnershipQualifier::Unknown); + template + AssignInstBase(SILDebugLocation DebugLoc, T&&...args) : + InstructionBase(DebugLoc), + Operands(this, std::forward(args)...) { } public: enum { @@ -3711,7 +3709,20 @@ class AssignInst ArrayRef getAllOperands() const { return Operands.asArray(); } MutableArrayRef getAllOperands() { return Operands.asArray(); } +}; + +/// AssignInst - Represents an abstract assignment to a memory location, which +/// may either be an initialization or a store sequence. This is only valid in +/// Raw SIL. +class AssignInst + : public AssignInstBase { + friend SILBuilder; + AssignInst(SILDebugLocation DebugLoc, SILValue Src, SILValue Dest, + AssignOwnershipQualifier Qualifier = + AssignOwnershipQualifier::Unknown); + +public: AssignOwnershipQualifier getOwnershipQualifier() const { return AssignOwnershipQualifier( SILInstruction::Bits.AssignInst.OwnershipQualifier); @@ -3721,6 +3732,32 @@ class AssignInst } }; +/// AssignByDelegateInst - Represents an abstract assignment via a delegate, +/// which may either be an initialization or a store sequence. This is only +/// valid in Raw SIL. +class AssignByDelegateInst + : public AssignInstBase { + friend SILBuilder; + + AssignByDelegateInst(SILDebugLocation DebugLoc, SILValue Src, SILValue Dest, + SILValue Initializer, SILValue Setter, + AssignOwnershipQualifier Qualifier = + AssignOwnershipQualifier::Unknown); + +public: + + SILValue getInitializer() { return Operands[2].get(); } + SILValue getSetter() { return Operands[3].get(); } + + AssignOwnershipQualifier getOwnershipQualifier() const { + return AssignOwnershipQualifier( + SILInstruction::Bits.AssignByDelegateInst.OwnershipQualifier); + } + void setOwnershipQualifier(AssignOwnershipQualifier qualifier) { + SILInstruction::Bits.AssignByDelegateInst.OwnershipQualifier = unsigned(qualifier); + } +}; + /// Indicates that a memory location is uninitialized at this point and needs to /// be initialized by the end of the function and before any escape point for /// this instruction. This is only valid in Raw SIL. diff --git a/include/swift/SIL/SILNode.h b/include/swift/SIL/SILNode.h index d582c4b3901ab..8a6f546f5b6e7 100644 --- a/include/swift/SIL/SILNode.h +++ b/include/swift/SIL/SILNode.h @@ -285,6 +285,10 @@ class alignas(8) SILNode { NumAssignOwnershipQualifierBits, OwnershipQualifier : NumAssignOwnershipQualifierBits ); + SWIFT_INLINE_BITFIELD(AssignByDelegateInst, NonValueInstruction, + NumAssignOwnershipQualifierBits, + OwnershipQualifier : NumAssignOwnershipQualifierBits + ); SWIFT_INLINE_BITFIELD(UncheckedOwnershipConversionInst,SingleValueInstruction, NumVOKindBits, diff --git a/include/swift/SIL/SILNodes.def b/include/swift/SIL/SILNodes.def index d90c2d1e85c4c..783235390098f 100644 --- a/include/swift/SIL/SILNodes.def +++ b/include/swift/SIL/SILNodes.def @@ -805,6 +805,8 @@ NON_VALUE_INST(StoreInst, store, SILInstruction, MayWrite, DoesNotRelease) NON_VALUE_INST(AssignInst, assign, SILInstruction, MayWrite, DoesNotRelease) +NON_VALUE_INST(AssignByDelegateInst, assign_by_delegate, + SILInstruction, MayWrite, DoesNotRelease) NON_VALUE_INST(MarkFunctionEscapeInst, mark_function_escape, SILInstruction, None, DoesNotRelease) NON_VALUE_INST(DebugValueInst, debug_value, diff --git a/include/swift/Sema/IDETypeChecking.h b/include/swift/Sema/IDETypeChecking.h index 94a3ef0703b2f..da15b41b7f7ca 100644 --- a/include/swift/Sema/IDETypeChecking.h +++ b/include/swift/Sema/IDETypeChecking.h @@ -239,6 +239,10 @@ namespace swift { /// \param DC The DeclContext from which the subscript is being referenced. Optional getRootTypeOfKeypathDynamicMember(SubscriptDecl *subscript, const DeclContext *DC); + + /// Determine whether the given property is part of the memberwise initializer + /// for a struct. + bool isMemberwiseInitialized(VarDecl *var); } #endif diff --git a/include/swift/Serialization/ModuleFormat.h b/include/swift/Serialization/ModuleFormat.h index e64af939471db..095446581bb96 100644 --- a/include/swift/Serialization/ModuleFormat.h +++ b/include/swift/Serialization/ModuleFormat.h @@ -52,7 +52,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 487; // Last change: Opaque result types generic params +const uint16_t SWIFTMODULE_VERSION_MINOR = 488; // assign_by_delegate using DeclIDField = BCFixed<31>; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index e2c6695ac693f..354b838d29732 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -31,6 +31,7 @@ #include "swift/AST/NameLookup.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PrettyStackTrace.h" +#include "swift/AST/PropertyDelegates.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/RawComment.h" #include "swift/AST/SubstitutionMap.h" @@ -283,6 +284,13 @@ FOR_KNOWN_FOUNDATION_TYPES(CACHE_FOUNDATION_DECL) llvm::DenseMap> DefaultTypeRequestCaches; + /// Mapping from property declarations to the backing variable types. + llvm::DenseMap PropertyDelegateBackingVarTypes; + + /// A mapping from the backing storage of a property that has a delegate + /// to the original property with the delegate. + llvm::DenseMap OriginalDelegatedProperties; + /// Structure that captures data that is segregated into different /// arenas. struct Arena { @@ -4350,3 +4358,43 @@ Type &ASTContext::getDefaultTypeRequestCache(SourceFile *SF, KnownProtocolKind kind) { return getImpl().DefaultTypeRequestCaches[SF][size_t(kind)]; } + +Type ASTContext::getSideCachedPropertyDelegateBackingPropertyType( + VarDecl *var) const { + return getImpl().PropertyDelegateBackingVarTypes[var]; +} + +void ASTContext::setSideCachedPropertyDelegateBackingPropertyType( + VarDecl *var, Type type) { + assert(!getImpl().PropertyDelegateBackingVarTypes[var] || + getImpl().PropertyDelegateBackingVarTypes[var]->isEqual(type)); + getImpl().PropertyDelegateBackingVarTypes[var] = type; +} + +VarDecl *VarDecl::getOriginalDelegatedProperty( + Optional kind) const { + if (!Bits.VarDecl.IsPropertyDelegateBackingProperty) + return nullptr; + + ASTContext &ctx = getASTContext(); + assert(ctx.getImpl().OriginalDelegatedProperties.count(this) > 0); + auto original = ctx.getImpl().OriginalDelegatedProperties[this]; + if (!kind) + return original; + + auto delegateInfo = original->getPropertyDelegateBackingPropertyInfo(); + switch (*kind) { + case PropertyDelegateSynthesizedPropertyKind::Backing: + return this == delegateInfo.backingVar ? original : nullptr; + + case PropertyDelegateSynthesizedPropertyKind::StorageDelegate: + return this == delegateInfo.storageDelegateVar ? original : nullptr; + } +} + +void VarDecl::setOriginalDelegatedProperty(VarDecl *originalProperty) { + Bits.VarDecl.IsPropertyDelegateBackingProperty = true; + ASTContext &ctx = getASTContext(); + assert(ctx.getImpl().OriginalDelegatedProperties.count(this) == 0); + ctx.getImpl().OriginalDelegatedProperties[this] = originalProperty; +} diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index 0d7c278253833..8dd0ff12bbd95 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -121,7 +121,32 @@ class Traversal : public ASTVisitorgetAttrs().getAttributes()) { + CustomAttr *mutableCustomAttr = const_cast(customAttr); + if (doIt(mutableCustomAttr->getTypeLoc())) + return true; + + if (auto semanticInit = customAttr->getSemanticInit()) { + if (auto newSemanticInit = doIt(semanticInit)) + mutableCustomAttr->setSemanticInit(newSemanticInit); + else + return true; + } else if (auto arg = customAttr->getArg()) { + if (auto newArg = doIt(arg)) + mutableCustomAttr->setArg(newArg); + else + return true; + } + } + + return false; + } + //===--------------------------------------------------------------------===// // Decls //===--------------------------------------------------------------------===// @@ -151,6 +176,16 @@ class Traversal : public ASTVisitorgetSingleVar()) { + if (visitCustomAttributes(singleVar)) + return true; + + isPropertyDelegateBackingProperty = + singleVar->getOriginalDelegatedProperty() != nullptr; + } + unsigned idx = 0U-1; for (auto entry : PBD->getPatternList()) { ++idx; @@ -159,7 +194,8 @@ class Traversal : public ASTVisitor "; if (hasDynamicSelfCapture()) OS << " "; + if (hasOpaqueValueCapture()) + OS << " "; interleave(getCaptures(), [&](const CapturedValue &capture) { diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 9430240ad8dd5..7e4fdfe8c7e54 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -35,12 +35,14 @@ #include "swift/AST/NameLookupRequests.h" #include "swift/AST/ParameterList.h" #include "swift/AST/Pattern.h" +#include "swift/AST/PropertyDelegates.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/ResilienceExpansion.h" #include "swift/AST/Stmt.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/AST/TypeLoc.h" #include "swift/AST/SwiftNameTranslation.h" +#include "swift/Parse/Lexer.h" #include "clang/Lex/MacroInfo.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallPtrSet.h" @@ -1339,6 +1341,22 @@ SourceRange PatternBindingEntry::getOrigInitRange() const { return Init ? Init->getSourceRange() : SourceRange(); } +bool PatternBindingEntry::isInitialized() const { + // Directly initialized. + if (getInit()) + return true; + + // Initialized via a property delegate. + if (auto var = getPattern()->getSingleVar()) { + if (auto customAttr = var->getAttachedPropertyDelegate()) { + if (customAttr->getArg() != nullptr) + return true; + } + } + + return false; +} + void PatternBindingEntry::setInit(Expr *E) { auto F = PatternAndFlags.getInt(); if (E) { @@ -1519,7 +1537,7 @@ bool PatternBindingDecl::isDefaultInitializable(unsigned i) const { const auto entry = getPatternList()[i]; // If it has an initializer expression, this is trivially true. - if (entry.getInit()) + if (entry.isInitialized()) return true; if (entry.getPattern()->isNeverDefaultInitializable()) @@ -3300,6 +3318,14 @@ Optional NominalTypeDecl::getKeyPathTypeKind() const { return None; } +PropertyDelegateTypeInfo NominalTypeDecl::getPropertyDelegateTypeInfo() const { + ASTContext &ctx = getASTContext(); + auto mutableThis = const_cast(this); + return evaluateOrDefault(ctx.evaluator, + PropertyDelegateTypeInfoRequest{mutableThis}, + PropertyDelegateTypeInfo()); +} + GenericTypeDecl::GenericTypeDecl(DeclKind K, DeclContext *DC, Identifier name, SourceLoc nameLoc, MutableArrayRef inherited, @@ -4897,7 +4923,7 @@ bool VarDecl::isSettable(const DeclContext *UseDC, // If the decl has an explicitly written initializer with a pattern binding, // then it isn't settable. - if (getParentInitializer() != nullptr) + if (isParentInitialized()) return false; // Normal lets (e.g. globals) are only mutable in the context of the @@ -5191,6 +5217,88 @@ StaticSpellingKind AbstractStorageDecl::getCorrectStaticSpelling() const { return getCorrectStaticSpellingForDecl(this); } +CustomAttr *VarDecl::getAttachedPropertyDelegate() const { + auto &ctx = getASTContext(); + if (!ctx.getLazyResolver()) + return nullptr; + + auto mutableThis = const_cast(this); + return evaluateOrDefault(ctx.evaluator, + AttachedPropertyDelegateRequest{mutableThis}, + nullptr); +} + +PropertyDelegateTypeInfo VarDecl::getAttachedPropertyDelegateTypeInfo() const { + auto attr = getAttachedPropertyDelegate(); + if (!attr) + return PropertyDelegateTypeInfo(); + + auto dc = getDeclContext(); + ASTContext &ctx = getASTContext(); + auto nominal = evaluateOrDefault( + ctx.evaluator, CustomAttrNominalRequest{attr, dc}, nullptr); + if (!nominal) + return PropertyDelegateTypeInfo(); + + return nominal->getPropertyDelegateTypeInfo(); +} + +Type VarDecl::getAttachedPropertyDelegateType() const { + auto &ctx = getASTContext(); + if (!ctx.getLazyResolver()) + return nullptr; + + auto mutableThis = const_cast(this); + return evaluateOrDefault(ctx.evaluator, + AttachedPropertyDelegateTypeRequest{mutableThis}, + Type()); +} + +Type VarDecl::getPropertyDelegateBackingPropertyType() const { + ASTContext &ctx = getASTContext(); + if (!ctx.getLazyResolver()) + return nullptr; + + auto mutableThis = const_cast(this); + return evaluateOrDefault( + ctx.evaluator, PropertyDelegateBackingPropertyTypeRequest{mutableThis}, + Type()); +} + +PropertyDelegateBackingPropertyInfo +VarDecl::getPropertyDelegateBackingPropertyInfo() const { + auto &ctx = getASTContext(); + if (!ctx.getLazyResolver()) + return PropertyDelegateBackingPropertyInfo(); + + auto mutableThis = const_cast(this); + return evaluateOrDefault( + ctx.evaluator, + PropertyDelegateBackingPropertyInfoRequest{mutableThis}, + PropertyDelegateBackingPropertyInfo()); +} + +VarDecl *VarDecl::getPropertyDelegateBackingProperty() const { + return getPropertyDelegateBackingPropertyInfo().backingVar; +} + +bool VarDecl::isPropertyDelegateInitializedWithInitialValue() const { + auto &ctx = getASTContext(); + if (!ctx.getLazyResolver()) + return false; + + // If there is no initializer, the initialization form depends on + // whether the property delegate type has an init(initialValue:). + if (!isParentInitialized()) { + auto delegateTypeInfo = getAttachedPropertyDelegateTypeInfo(); + return delegateTypeInfo.initialValueInit != nullptr; + } + + // Otherwise, check whether the '=' initialization form was used. + return getPropertyDelegateBackingPropertyInfo().originalInitialValue + != nullptr; +} + Identifier VarDecl::getObjCPropertyName() const { if (auto attr = getAttrs().getAttribute()) { if (auto name = attr->getName()) @@ -5415,6 +5523,31 @@ void ParamDecl::setDefaultArgumentInitContext(Initializer *initContext) { DefaultValueAndFlags.getPointer()->InitContext = initContext; } +Expr *swift::findOriginalPropertyDelegateInitialValue(VarDecl *var, + Expr *init) { + auto attr = var->getAttachedPropertyDelegate(); + assert(attr && "No attached property delegate?"); + + // Direct initialization implies no original initial value. + if (attr->getArg()) + return nullptr; + + // Look through any expressions wrapping the initializer. + init = init->getSemanticsProvidingExpr(); + auto initCall = dyn_cast(init); + if (!initCall) + return nullptr; + + auto initArg = cast(initCall->getArg())->getElement(0); + initArg = initArg->getSemanticsProvidingExpr(); + if (auto autoclosure = dyn_cast(initArg)) { + initArg = + autoclosure->getSingleExpressionBody()->getSemanticsProvidingExpr(); + } + + return initArg; +} + StringRef ParamDecl::getDefaultValueStringRepresentation( SmallVectorImpl &scratch) const { @@ -5445,6 +5578,23 @@ ParamDecl::getDefaultValueStringRepresentation( if (!existing.empty()) return existing; auto var = getStoredProperty(); + + if (auto original = var->getOriginalDelegatedProperty()) { + if (auto attr = original->getAttachedPropertyDelegate()) { + if (auto arg = attr->getArg()) { + SourceRange fullRange(attr->getTypeLoc().getSourceRange().Start, + arg->getEndLoc()); + auto charRange = Lexer::getCharSourceRangeFromSourceRange( + getASTContext().SourceMgr, fullRange); + return getASTContext().SourceMgr.extractText(charRange); + } + + auto init = findOriginalPropertyDelegateInitialValue( + original, original->getParentInitializer()); + return extractInlinableText(getASTContext().SourceMgr, init, scratch); + } + } + return extractInlinableText(getASTContext().SourceMgr, var->getParentInitializer(), scratch); diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 4f77e07349d6e..d3d8995b6164f 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -14,6 +14,7 @@ #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticsCommon.h" #include "swift/AST/Module.h" +#include "swift/AST/PropertyDelegates.h" #include "swift/AST/TypeLoc.h" #include "swift/AST/TypeRepr.h" #include "swift/AST/Types.h" @@ -546,3 +547,116 @@ bool DefaultTypeRequest::getPerformLocalLookup(const KnownProtocolKind knownProt default: return false; } } + +bool PropertyDelegateTypeInfoRequest::isCached() const { + auto nominal = std::get<0>(getStorage()); + return nominal->getAttrs().hasAttribute();; +} + +void PropertyDelegateTypeInfoRequest::diagnoseCycle( + DiagnosticEngine &diags) const { + std::get<0>(getStorage())->diagnose(diag::circular_reference); +} + +void PropertyDelegateTypeInfoRequest::noteCycleStep( + DiagnosticEngine &diags) const { + std::get<0>(getStorage())->diagnose(diag::circular_reference_through); +} + +bool AttachedPropertyDelegateRequest::isCached() const { + auto var = std::get<0>(getStorage()); + return !var->getAttrs().isEmpty(); +} + +void AttachedPropertyDelegateRequest::diagnoseCycle( + DiagnosticEngine &diags) const { + std::get<0>(getStorage())->diagnose(diag::circular_reference); +} + +void AttachedPropertyDelegateRequest::noteCycleStep( + DiagnosticEngine &diags) const { + std::get<0>(getStorage())->diagnose(diag::circular_reference_through); +} + +bool AttachedPropertyDelegateTypeRequest::isCached() const { + auto var = std::get<0>(getStorage()); + return !var->getAttrs().isEmpty(); +} + +void AttachedPropertyDelegateTypeRequest::diagnoseCycle( + DiagnosticEngine &diags) const { + std::get<0>(getStorage())->diagnose(diag::circular_reference); +} + +void AttachedPropertyDelegateTypeRequest::noteCycleStep( + DiagnosticEngine &diags) const { + std::get<0>(getStorage())->diagnose(diag::circular_reference_through); +} + +bool PropertyDelegateBackingPropertyTypeRequest::isCached() const { + auto var = std::get<0>(getStorage()); + return !var->getAttrs().isEmpty(); +} + +void PropertyDelegateBackingPropertyTypeRequest::diagnoseCycle( + DiagnosticEngine &diags) const { + std::get<0>(getStorage())->diagnose(diag::circular_reference); +} + +void PropertyDelegateBackingPropertyTypeRequest::noteCycleStep( + DiagnosticEngine &diags) const { + std::get<0>(getStorage())->diagnose(diag::circular_reference_through); +} +bool PropertyDelegateBackingPropertyInfoRequest::isCached() const { + auto var = std::get<0>(getStorage()); + return !var->getAttrs().isEmpty(); +} + +void PropertyDelegateBackingPropertyInfoRequest::diagnoseCycle( + DiagnosticEngine &diags) const { + std::get<0>(getStorage())->diagnose(diag::circular_reference); +} + +void PropertyDelegateBackingPropertyInfoRequest::noteCycleStep( + DiagnosticEngine &diags) const { + std::get<0>(getStorage())->diagnose(diag::circular_reference_through); +} + +void swift::simple_display( + llvm::raw_ostream &out, const PropertyDelegateTypeInfo &propertyDelegate) { + out << "{ "; + if (propertyDelegate.valueVar) + out << propertyDelegate.valueVar->printRef(); + else + out << "null"; + out << ", "; + if (propertyDelegate.initialValueInit) + out << propertyDelegate.initialValueInit->printRef(); + else + out << "null"; + out << " }"; +} + +void swift::simple_display( + llvm::raw_ostream &out, + const PropertyDelegateBackingPropertyInfo &backingInfo) { + out << "{ "; + if (backingInfo.backingVar) + backingInfo.backingVar->dumpRef(out); + out << " }"; +} + + +template<> +bool swift::AnyValue::Holder::equals(const HolderBase &other) const { + assert(typeID == other.typeID && "Caller should match type IDs"); + return value.getPointer() == + static_cast &>(other).value.getPointer(); +} + +void swift::simple_display(llvm::raw_ostream &out, const Type &type) { + if (type) + type.print(out); + else + out << "null"; +} diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index a87e7939c3d65..2708f36b0f72b 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -933,6 +933,9 @@ class IRGenSILFunction : void visitAssignInst(AssignInst *i) { llvm_unreachable("assign is not valid in canonical SIL"); } + void visitAssignByDelegateInst(AssignByDelegateInst *i) { + llvm_unreachable("assign_by_delegate is not valid in canonical SIL"); + } void visitMarkUninitializedInst(MarkUninitializedInst *i) { llvm_unreachable("mark_uninitialized is not valid in canonical SIL"); } diff --git a/lib/Index/Index.cpp b/lib/Index/Index.cpp index 31c39f564e6bc..1ceb8982b4118 100644 --- a/lib/Index/Index.cpp +++ b/lib/Index/Index.cpp @@ -334,6 +334,7 @@ class IndexSwiftASTWalker : public SourceEntityWalker { // get label locations auto *MemberwiseInit = DeclRef->getDecl(); std::vector LabelLocs; + ArrayRef Labels; auto NameLoc = DeclRef->getNameLoc(); if (NameLoc.isCompound()) { size_t LabelIndex = 0; @@ -341,27 +342,39 @@ class IndexSwiftASTWalker : public SourceEntityWalker { while ((ArgLoc = NameLoc.getArgumentLabelLoc(LabelIndex++)).isValid()) { LabelLocs.push_back(ArgLoc); } + Labels = MemberwiseInit->getFullName().getArgumentNames(); } else if (auto *CallParent = dyn_cast_or_null(getParentExpr())) { LabelLocs = CallParent->getArgumentLabelLocs(); + Labels = CallParent->getArgumentLabels(); } if (LabelLocs.empty()) return; + assert(Labels.size() == LabelLocs.size()); + // match labels to properties auto *TypeContext = MemberwiseInit->getDeclContext()->getSelfNominalTypeDecl(); if (!TypeContext || !shouldIndex(TypeContext, false)) return; - auto LabelIt = LabelLocs.begin(); + unsigned CurLabel = 0; for (auto Prop : TypeContext->getStoredProperties()) { - if (Prop->getParentInitializer() && Prop->isLet()) + if (auto Original = Prop->getOriginalDelegatedProperty()) + Prop = Original; + + if (!isMemberwiseInitialized(Prop)) + continue; + + if (CurLabel == LabelLocs.size()) + break; + + if (Labels[CurLabel] != Prop->getName()) continue; - assert(LabelIt != LabelLocs.end()); IndexSymbol Info; - if (initIndexSymbol(Prop, *LabelIt++, /*IsRef=*/true, Info)) + if (initIndexSymbol(Prop, LabelLocs[CurLabel++], /*IsRef=*/true, Info)) continue; if (startEntity(Prop, Info, /*IsRef=*/true)) finishCurrentEntity(); diff --git a/lib/Parse/Lexer.cpp b/lib/Parse/Lexer.cpp index 1caa40abc68b4..afe5f9216160e 100644 --- a/lib/Parse/Lexer.cpp +++ b/lib/Parse/Lexer.cpp @@ -945,16 +945,7 @@ void Lexer::lexDollarIdent() { return formToken(tok::identifier, tokStart); } - // We reserve $nonNumeric for persistent bindings in the debugger and implicit - // variables, like storage for lazy properties. if (!isAllDigits) { - if (!LangOpts.EnableDollarIdentifiers && !InSILBody && - LexMode != LexerMode::SwiftInterface) - diagnose(tokStart, diag::expected_dollar_numeric); - - // Even if we diagnose, we go ahead and form an identifier token, - // in part to ensure that the basic behavior of the lexer is - // independent of language mode. return formToken(tok::identifier, tokStart); } else { return formToken(tok::dollarident, tokStart); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index f84b59c1cac3d..77f7f7c779e2b 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1243,9 +1243,8 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, return false; } - Identifier name = Context.getIdentifier(Tok.getText()); - - consumeToken(tok::identifier); + Identifier name; + consumeIdentifier(&name); auto range = SourceRange(Loc, Tok.getRange().getStart()); @@ -1712,6 +1711,18 @@ bool Parser::parseVersionTuple(llvm::VersionTuple &Version, return false; } +/// Check whether the attributes have already established an initializer +/// context within the given set of attributes. +static PatternBindingInitializer *findAttributeInitContent( + DeclAttributes &Attributes) { + for (auto custom : Attributes.getAttributes()) { + if (auto initContext = custom->getInitContext()) + return initContext; + } + + return nullptr; +} + /// \verbatim /// attribute: /// '_silgen_name' '(' identifier ')' @@ -1854,7 +1865,10 @@ bool Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc) { // DeclContexts for any closures that may live inside of initializers. Optional initParser; if (!CurDeclContext->isLocalContext()) { - initContext = new (Context) PatternBindingInitializer(CurDeclContext); + initContext = findAttributeInitContent(Attributes); + if (!initContext) + initContext = new (Context) PatternBindingInitializer(CurDeclContext); + initParser.emplace(*this, initContext); } @@ -4251,8 +4265,7 @@ static ParameterList *parseOptionalAccessorArgument(SourceLoc SpecifierLoc, EndLoc = StartLoc; } else { // We have a name. - Name = P.Context.getIdentifier(P.Tok.getText()); - NameLoc = P.consumeToken(); + NameLoc = P.consumeIdentifier(&Name); auto DiagID = Kind == AccessorKind::Set ? diag::expected_rparen_set_name : @@ -5244,10 +5257,14 @@ Parser::parseDeclVar(ParseDeclOptions Flags, } }); + // Check whether we have already established an initializer context. + PatternBindingInitializer *initContext = + findAttributeInitContent(Attributes); + // Remember this pattern/init pair for our ultimate PatternBindingDecl. The // Initializer will be added later when/if it is parsed. PBDEntries.push_back({pattern, /*EqualLoc*/ SourceLoc(), /*Init*/ nullptr, - /*InitContext*/ nullptr}); + initContext}); Expr *PatternInit = nullptr; @@ -5257,7 +5274,6 @@ Parser::parseDeclVar(ParseDeclOptions Flags, // If we're not in a local context, we'll need a context to parse initializers // into (should we have one). This happens for properties and global // variables in libraries. - PatternBindingInitializer *initContext = nullptr; // Record the variables that we're trying to initialize. This allows us // to cleanly reject "var x = x" when "x" isn't bound to an enclosing @@ -5322,7 +5338,7 @@ Parser::parseDeclVar(ParseDeclOptions Flags, // If the attributes include @lazy, flag that on each initializer. if (Attributes.hasAttribute()) { - PBDEntries.back().setInitializerLazy(); + PBDEntries.back().setInitializerSubsumed(); } if (init.hasCodeCompletion()) { @@ -6734,15 +6750,17 @@ Parser::parseDeclOperatorImpl(SourceLoc OperatorLoc, Identifier Name, SyntaxParsingContext GroupCtxt(SyntaxContext, SyntaxKind::IdentifierList); - identifiers.push_back(Context.getIdentifier(Tok.getText())); - identifierLocs.push_back(consumeToken(tok::identifier)); + Identifier name; + identifierLocs.push_back(consumeIdentifier(&name)); + identifiers.push_back(name); while (Tok.is(tok::comma)) { auto comma = consumeToken(); if (Tok.is(tok::identifier)) { - identifiers.push_back(Context.getIdentifier(Tok.getText())); - identifierLocs.push_back(consumeToken(tok::identifier)); + Identifier name; + identifierLocs.push_back(consumeIdentifier(&name)); + identifiers.push_back(name); } else { if (Tok.isNot(tok::eof)) { auto otherTokLoc = consumeToken(); @@ -7034,8 +7052,9 @@ Parser::parseDeclPrecedenceGroup(ParseDeclOptions flags, diagnose(Tok, diag::expected_precedencegroup_relation, attrName); return abortBody(); } - auto name = Context.getIdentifier(Tok.getText()); - relations.push_back({consumeToken(), name, nullptr}); + Identifier name; + SourceLoc nameLoc = consumeIdentifier(&name); + relations.push_back({nameLoc, name, nullptr}); if (skipUnspacedCodeCompleteToken()) return abortBody(/*hasCodeCompletion*/true); diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 02ddea163c14d..93101ecdc61e0 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -1548,7 +1548,7 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { // call pattern. peekToken().isNot(tok::period, tok::period_prefix, tok::l_paren)) { Identifier name; - SourceLoc loc = consumeIdentifier(&name); + SourceLoc loc = consumeIdentifier(&name, /*allowDollarIdentifier=*/true); auto specifier = (InVarOrLetPattern != IVOLP_InVar) ? VarDecl::Specifier::Let : VarDecl::Specifier::Var; @@ -2132,8 +2132,10 @@ DeclName Parser::parseUnqualifiedDeclName(bool afterDot, DeclBaseName baseName; SourceLoc baseNameLoc; if (Tok.isAny(tok::identifier, tok::kw_Self, tok::kw_self)) { - baseName = Context.getIdentifier(Tok.getText()); - baseNameLoc = consumeToken(); + Identifier baseNameId; + baseNameLoc = consumeIdentifier( + &baseNameId, /*allowDollarIdentifier=*/true); + baseName = baseNameId; } else if (allowOperators && Tok.isAnyOperator()) { baseName = Context.getIdentifier(Tok.getText()); baseNameLoc = consumeToken(); @@ -2643,13 +2645,17 @@ parseClosureSignatureIfPresent(SmallVectorImpl &captureList, break; } - Identifier name = Tok.is(tok::identifier) ? - Context.getIdentifier(Tok.getText()) : Identifier(); + Identifier name; + SourceLoc nameLoc; + if (Tok.is(tok::identifier)) { + nameLoc = consumeIdentifier(&name); + } else { + nameLoc = consumeToken(tok::kw__); + } auto var = new (Context) ParamDecl(VarDecl::Specifier::Default, SourceLoc(), SourceLoc(), - Identifier(), Tok.getLoc(), name, nullptr); + Identifier(), nameLoc, name, nullptr); elements.push_back(var); - consumeToken(); // Consume a comma to continue. HasNext = consumeIf(tok::comma); diff --git a/lib/Parse/ParseGeneric.cpp b/lib/Parse/ParseGeneric.cpp index 9667768a7d7da..0c8be3302500f 100644 --- a/lib/Parse/ParseGeneric.cpp +++ b/lib/Parse/ParseGeneric.cpp @@ -297,8 +297,8 @@ ParserStatus Parser::parseGenericWhereClause( getLayoutConstraint(Context.getIdentifier(Tok.getText()), Context) ->isKnownLayout()) { // Parse a layout constraint. - auto LayoutName = Context.getIdentifier(Tok.getText()); - auto LayoutLoc = consumeToken(); + Identifier LayoutName; + auto LayoutLoc = consumeIdentifier(&LayoutName); auto LayoutInfo = parseLayoutConstraint(LayoutName); if (!LayoutInfo->isKnownLayout()) { // There was a bug in the layout constraint. diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 1effd9dc30b03..fa7efc6cd0d09 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -864,7 +864,12 @@ bool Parser::parseSpecificIdentifier(StringRef expected, SourceLoc &loc, /// its name in Result. Otherwise, emit an error and return true. bool Parser::parseAnyIdentifier(Identifier &Result, SourceLoc &Loc, const Diagnostic &D) { - if (Tok.is(tok::identifier) || Tok.isAnyOperator()) { + if (Tok.is(tok::identifier)) { + Loc = consumeIdentifier(&Result); + return false; + } + + if (Tok.isAnyOperator()) { Result = Context.getIdentifier(Tok.getText()); Loc = Tok.getLoc(); consumeToken(); diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index c5226b31b296c..48d9286272193 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -3540,6 +3540,34 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { break; } + case SILInstructionKind::AssignByDelegateInst: { + SILValue Src, DestAddr, InitFn, SetFn; + SourceLoc DestLoc; + AssignOwnershipQualifier AssignQualifier; + if (parseTypedValueRef(Src, B) || + parseVerbatim("to") || + parseAssignOwnershipQualifier(AssignQualifier, *this) || + parseTypedValueRef(DestAddr, DestLoc, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseVerbatim("init") || + parseTypedValueRef(InitFn, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseVerbatim("set") || + parseTypedValueRef(SetFn, B) || + parseSILDebugLocation(InstLoc, B)) + return true; + + if (!DestAddr->getType().isAddress()) { + P.diagnose(DestLoc, diag::sil_operand_not_address, "destination", + OpcodeName); + return true; + } + + ResultVal = B.createAssignByDelegate(InstLoc, Src, DestAddr, InitFn, SetFn, + AssignQualifier); + break; + } + case SILInstructionKind::BeginAccessInst: case SILInstructionKind::BeginUnpairedAccessInst: case SILInstructionKind::EndAccessInst: diff --git a/lib/SIL/InstructionUtils.cpp b/lib/SIL/InstructionUtils.cpp index 413c25a9f410f..067c4178dac54 100644 --- a/lib/SIL/InstructionUtils.cpp +++ b/lib/SIL/InstructionUtils.cpp @@ -377,6 +377,21 @@ SILValue swift::isPartialApplyOfReabstractionThunk(PartialApplyInst *PAI) { return Arg; } +bool swift::onlyUsedByAssignByDelegate(PartialApplyInst *PAI) { + bool usedByAssignByDelegate = false; + for (Operand *Op : PAI->getUses()) { + SILInstruction *User = Op->getUser(); + if (isa(User) && Op->getOperandNumber() >= 2) { + usedByAssignByDelegate = true; + continue; + } + if (isa(User)) + continue; + return false; + } + return usedByAssignByDelegate; +} + /// Given a block used as a noescape function argument, attempt to find all /// Swift closures that invoking the block will call. The StoredClosures may not /// actually be partial_apply instructions. They may be copied, block arguments, diff --git a/lib/SIL/MemAccessUtils.cpp b/lib/SIL/MemAccessUtils.cpp index 7fdb51295cc23..28c540fc98163 100644 --- a/lib/SIL/MemAccessUtils.cpp +++ b/lib/SIL/MemAccessUtils.cpp @@ -678,6 +678,7 @@ void swift::visitAccessedAddress(SILInstruction *I, llvm_unreachable("unexpected memory access."); case SILInstructionKind::AssignInst: + case SILInstructionKind::AssignByDelegateInst: visitor(&I->getAllOperands()[AssignInst::Dest]); return; diff --git a/lib/SIL/OperandOwnership.cpp b/lib/SIL/OperandOwnership.cpp index b0ac2139ca1f7..e6694dbd3ac67 100644 --- a/lib/SIL/OperandOwnership.cpp +++ b/lib/SIL/OperandOwnership.cpp @@ -840,6 +840,16 @@ OperandOwnershipKindClassifier::visitAssignInst(AssignInst *i) { UseLifetimeConstraint::MustBeInvalidated); } +OperandOwnershipKindMap +OperandOwnershipKindClassifier::visitAssignByDelegateInst(AssignByDelegateInst *i) { + if (getValue() != i->getSrc()) { + return Map::allLive(); + } + + return Map::compatibilityMap(ValueOwnershipKind::Owned, + UseLifetimeConstraint::MustBeInvalidated); +} + OperandOwnershipKindMap OperandOwnershipKindClassifier::visitStoreInst(StoreInst *i) { if (getValue() != i->getSrc()) { diff --git a/lib/SIL/SILFunctionType.cpp b/lib/SIL/SILFunctionType.cpp index 648b8bad7ef5f..ab1b1c7284201 100644 --- a/lib/SIL/SILFunctionType.cpp +++ b/lib/SIL/SILFunctionType.cpp @@ -743,6 +743,27 @@ lowerCaptureContextParameters(SILModule &M, AnyFunctionRef function, continue; } + if (capture.isOpaqueValue()) { + OpaqueValueExpr *opaqueValue = capture.getOpaqueValue(); + auto canType = opaqueValue->getType()->mapTypeOutOfContext() + ->getCanonicalType(origGenericSig); + auto &loweredTL = + Types.getTypeLowering(AbstractionPattern(genericSig, canType), + canType, expansion); + auto loweredTy = loweredTL.getLoweredType(); + + ParameterConvention convention; + if (loweredTL.isAddressOnly()) { + convention = ParameterConvention::Indirect_In; + } else { + convention = ParameterConvention::Direct_Owned; + } + SILParameterInfo param(loweredTy.getASTType(), convention); + inputs.push_back(param); + + continue; + } + auto *VD = capture.getDecl(); auto type = VD->getInterfaceType(); auto canType = type->getCanonicalType(origGenericSig); diff --git a/lib/SIL/SILInstructions.cpp b/lib/SIL/SILInstructions.cpp index 8df99bb2b6764..cda5c617f7a3b 100644 --- a/lib/SIL/SILInstructions.cpp +++ b/lib/SIL/SILInstructions.cpp @@ -813,11 +813,22 @@ StringRef swift::getSILAccessEnforcementName(SILAccessEnforcement enforcement) { } AssignInst::AssignInst(SILDebugLocation Loc, SILValue Src, SILValue Dest, - AssignOwnershipQualifier Qualifier) - : InstructionBase(Loc), Operands(this, Src, Dest) { + AssignOwnershipQualifier Qualifier) : + AssignInstBase(Loc, Src, Dest) { SILInstruction::Bits.AssignInst.OwnershipQualifier = unsigned(Qualifier); } +AssignByDelegateInst::AssignByDelegateInst(SILDebugLocation Loc, + SILValue Src, SILValue Dest, + SILValue Initializer, + SILValue Setter, + AssignOwnershipQualifier Qualifier) : + AssignInstBase(Loc, Src, Dest, Initializer, Setter) { + assert(Initializer->getType().is()); + SILInstruction::Bits.AssignByDelegateInst.OwnershipQualifier = + unsigned(Qualifier); +} + MarkFunctionEscapeInst * MarkFunctionEscapeInst::create(SILDebugLocation Loc, ArrayRef Elements, SILFunction &F) { diff --git a/lib/SIL/SILPrinter.cpp b/lib/SIL/SILPrinter.cpp index c353d48f44f8c..85c84369a1d0a 100644 --- a/lib/SIL/SILPrinter.cpp +++ b/lib/SIL/SILPrinter.cpp @@ -1309,6 +1309,14 @@ class SILPrinter : public SILInstructionVisitor { *this << getIDAndType(AI->getDest()); } + void visitAssignByDelegateInst(AssignByDelegateInst *AI) { + *this << getIDAndType(AI->getSrc()) << " to "; + printAssignOwnershipQualifier(AI->getOwnershipQualifier()); + *this << getIDAndType(AI->getDest()) + << ", init " << getIDAndType(AI->getInitializer()) + << ", set " << getIDAndType(AI->getSetter()); + } + void visitMarkUninitializedInst(MarkUninitializedInst *MU) { switch (MU->getKind()) { case MarkUninitializedInst::Var: *this << "[var] "; break; diff --git a/lib/SIL/SILProfiler.cpp b/lib/SIL/SILProfiler.cpp index 050682d20ba1c..7631898979038 100644 --- a/lib/SIL/SILProfiler.cpp +++ b/lib/SIL/SILProfiler.cpp @@ -145,7 +145,7 @@ static void walkPatternForProfiling(PatternBindingDecl *PBD, ASTWalker &Walker) { if (PBD && !PBD->isStatic()) for (auto E : PBD->getPatternList()) - if (auto init = E.getNonLazyInit()) + if (auto init = E.getExecutableInit()) init->walk(Walker); } diff --git a/lib/SIL/SILVerifier.cpp b/lib/SIL/SILVerifier.cpp index b2baa10fc12e1..80175772128c6 100644 --- a/lib/SIL/SILVerifier.cpp +++ b/lib/SIL/SILVerifier.cpp @@ -1761,6 +1761,53 @@ class SILVerifier : public SILVerifierBase { "Store operand type and dest type mismatch"); } + void checkAssignByDelegateInst(AssignByDelegateInst *AI) { + SILValue Src = AI->getSrc(), Dest = AI->getDest(); + require(AI->getModule().getStage() == SILStage::Raw, + "assign instruction can only exist in raw SIL"); + require(Dest->getType().isAddress(), "Must store to an address dest"); + + unsigned indirectInitResults = Src->getType().isAddress() ? 1 : 0; + + SILValue initFn = AI->getInitializer(); + CanSILFunctionType initTy = initFn->getType().castTo(); + SILFunctionConventions initConv(initTy, AI->getModule()); + require(initConv.getNumIndirectSILResults() == indirectInitResults, + "init function has wrong number of indirect results"); + unsigned firstArgIdx = initConv.getSILArgIndexOfFirstParam(); + require(initConv.getNumSILArguments() == firstArgIdx + 1, + "init function has wrong number of arguments"); + require(Src->getType() == initConv.getSILArgumentType(firstArgIdx), + "wrong argument type of init function"); + switch (initConv.getNumIndirectSILResults()) { + case 0: + require(initConv.getNumDirectSILResults() == 1, + "wrong number of init function results"); + require(Dest->getType().getObjectType() == + initConv.getDirectSILResultTypes().front(), + "wrong init function result type"); + break; + case 1: + require(initConv.getNumDirectSILResults() == 0, + "wrong number of init function results"); + require(Dest->getType() == initConv.getIndirectSILResultTypes().front(), + "wrong indirect init function result type"); + break; + default: + require(false, "wrong number of indirect init function results"); + } + + SILValue setterFn = AI->getSetter(); + CanSILFunctionType setterTy = setterFn->getType().castTo(); + SILFunctionConventions setterConv(setterTy, AI->getModule()); + require(setterConv.getNumIndirectSILResults() == 0, + "set function has indirect results"); + require(setterConv.getNumSILArguments() == 1, + "init function has wrong number of arguments"); + require(Src->getType() == setterConv.getSILArgumentType(0), + "wrong argument type of init function"); + } + #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \ void checkLoad##Name##Inst(Load##Name##Inst *LWI) { \ require(LWI->getType().isObject(), "Result of load must be an object"); \ diff --git a/lib/SIL/TypeLowering.cpp b/lib/SIL/TypeLowering.cpp index f6c941145fac5..50d14f81ea0c0 100644 --- a/lib/SIL/TypeLowering.cpp +++ b/lib/SIL/TypeLowering.cpp @@ -22,6 +22,7 @@ #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" #include "swift/AST/Pattern.h" +#include "swift/AST/PropertyDelegates.h" #include "swift/AST/Types.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/SIL/SILArgument.h" @@ -1773,6 +1774,15 @@ static CanAnyFunctionType getStoredPropertyInitializerInterfaceType( CanType resultTy = VD->getParentPattern()->getType()->mapTypeOutOfContext() ->getCanonicalType(); + + // If this is the backing storage for a property with an attached + // delegate that was initialized with '=', the stored property initializer + // will be in terms of the original property's type. + if (auto originalProperty = VD->getOriginalDelegatedProperty()) { + if (originalProperty->isPropertyDelegateInitializedWithInitialValue()) + resultTy = originalProperty->getValueInterfaceType()->getCanonicalType(); + } + auto sig = TC.getEffectiveGenericSignature(DC); return CanAnyFunctionType::get(sig, {}, resultTy); @@ -2110,6 +2120,7 @@ CaptureInfo TypeConverter::getLoweredLocalCaptures(AnyFunctionRef fn) { // First, bail out if there are no local captures at all. if (!fn.getCaptureInfo().hasLocalCaptures() && + !fn.getCaptureInfo().hasOpaqueValueCapture() && !fn.getCaptureInfo().hasDynamicSelfCapture()) { CaptureInfo info; info.setGenericParamCaptures( @@ -2132,7 +2143,8 @@ TypeConverter::getLoweredLocalCaptures(AnyFunctionRef fn) { bool capturesGenericParams = false; DynamicSelfType *capturesDynamicSelf = nullptr; - + OpaqueValueExpr *capturesOpaqueValue = nullptr; + std::function collectFunctionCaptures = [&](AnyFunctionRef curFn) { if (!visitedFunctions.insert(curFn).second) @@ -2142,6 +2154,8 @@ TypeConverter::getLoweredLocalCaptures(AnyFunctionRef fn) { capturesGenericParams = true; if (curFn.getCaptureInfo().hasDynamicSelfCapture()) capturesDynamicSelf = curFn.getCaptureInfo().getDynamicSelfType(); + if (curFn.getCaptureInfo().hasOpaqueValueCapture()) + capturesOpaqueValue = curFn.getCaptureInfo().getOpaqueValue(); SmallVector localCaptures; curFn.getCaptureInfo().getLocalCaptures(localCaptures); @@ -2256,6 +2270,11 @@ TypeConverter::getLoweredLocalCaptures(AnyFunctionRef fn) { resultingCaptures.push_back(capturePair.second); } + // If we captured an opaque value, add it. + if (capturesOpaqueValue) { + resultingCaptures.push_back(CapturedValue(capturesOpaqueValue, 0)); + } + // If we captured the dynamic 'Self' type and we have a 'self' value also, // add it as the final capture. Otherwise, add a fake hidden capture for // the dynamic 'Self' metatype. @@ -2272,6 +2291,7 @@ TypeConverter::getLoweredLocalCaptures(AnyFunctionRef fn) { auto &cachedCaptures = inserted.first->second; cachedCaptures.setGenericParamCaptures(capturesGenericParams); cachedCaptures.setDynamicSelfType(capturesDynamicSelf); + cachedCaptures.setOpaqueValue(capturesOpaqueValue); cachedCaptures.setCaptures(Context.AllocateCopy(resultingCaptures)); return cachedCaptures; diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 0515316222a14..d86d9d7b5d0cc 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -22,8 +22,10 @@ #include "swift/AST/NameLookup.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PrettyStackTrace.h" +#include "swift/AST/PropertyDelegates.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/ResilienceExpansion.h" +#include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Statistic.h" #include "swift/Basic/Timer.h" #include "swift/ClangImporter/ClangModule.h" @@ -944,7 +946,7 @@ static bool requiresIVarInitialization(SILGenModule &SGM, ClassDecl *cd) { if (!pbd) continue; for (auto entry : pbd->getPatternList()) - if (entry.getNonLazyInit()) + if (entry.getExecutableInit()) return true; } @@ -1113,7 +1115,16 @@ emitStoredPropertyInitialization(PatternBindingDecl *pbd, unsigned i) { auto *var = pbdEntry.getAnchoringVarDecl(); auto *init = pbdEntry.getInit(); auto *initDC = pbdEntry.getInitContext(); - assert(!pbdEntry.isInitializerLazy()); + assert(!pbdEntry.isInitializerSubsumed()); + + // If this is the backing storage for a property with an attached delegate + // that was initialized with `=`, use that expression as the initializer. + if (auto originalProperty = var->getOriginalDelegatedProperty()) { + auto delegateInfo = + originalProperty->getPropertyDelegateBackingPropertyInfo(); + if (delegateInfo.originalInitialValue) + init = delegateInfo.originalInitialValue; + } SILDeclRef constant(var, SILDeclRef::Kind::StoredPropertyInitializer); emitOrDelayFunction(*this, constant, @@ -1279,7 +1290,7 @@ void SILGenModule::emitObjCDestructorThunk(DestructorDecl *destructor) { void SILGenModule::visitPatternBindingDecl(PatternBindingDecl *pd) { assert(!TopLevelSGF && "script mode PBDs should be in TopLevelCodeDecls"); for (unsigned i = 0, e = pd->getNumPatternEntries(); i != e; ++i) - if (pd->getNonLazyInit(i)) + if (pd->getExecutableInit(i)) emitGlobalInitialization(pd, i); } diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index aca29a051159b..024544be1bea0 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -4930,6 +4930,30 @@ RValue SILGenFunction::emitApplyAllocatingInitializer(SILLocation loc, return result; } +RValue SILGenFunction::emitApplyPropertyDelegateAllocator(SILLocation loc, + SubstitutionMap subs, + SILDeclRef ctorRef, + Type delegateTy, + CanAnyFunctionType funcTy) { + Callee callee = Callee::forDirect(*this, ctorRef, subs, loc); + + MetatypeType *MTty = MetatypeType::get(delegateTy); + auto metatypeVal = B.createMetatype(loc, getLoweredType(MTty)); + ManagedValue mtManagedVal = ManagedValue::forUnmanaged(metatypeVal); + RValue metatypeRVal(*this, loc, MTty->getCanonicalType(), mtManagedVal); + + ArgumentSource ArgSrc(loc, std::move(metatypeRVal)); + FormalEvaluationScope writebacks(*this); + CallEmission emission(*this, std::move(callee), std::move(writebacks)); + + AnyFunctionType::Param selfParam((Type(MTty)), Identifier(), + ParameterTypeFlags()); + emission.addSelfParam(loc, std::move(ArgSrc), selfParam, funcTy.getResult()); + + RValue RV = emission.apply(); + return RV; +} + /// Emit a literal that applies the various initializers. RValue SILGenFunction::emitLiteral(LiteralExpr *literal, SGFContext C) { ConcreteDeclRef builtinInit; diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 2369d06fe7d7f..c8a2fa994bdad 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -20,6 +20,7 @@ #include "swift/AST/ASTMangler.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/ParameterList.h" +#include "swift/AST/PropertyDelegates.h" #include "swift/Basic/Defer.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILUndef.h" @@ -97,6 +98,36 @@ static RValue emitImplicitValueConstructorArg(SILGenFunction &SGF, return RValue(SGF, loc, type, mvArg); } +/// If the field has a property delegate for which we will need to call the +/// delegate type's init(initialValue:), set up that evaluation and call the +/// \c body with the expression to form the property delegate instance from +/// the initial value type. +/// +/// \returns true if this was such a delegate, \c false otherwise. +static bool maybeEmitPropertyDelegateInitFromValue( + SILGenFunction &SGF, + SILLocation loc, + VarDecl *field, + RValue &&arg, + llvm::function_ref body) { + auto originalProperty = field->getOriginalDelegatedProperty(); + if (!originalProperty || + !originalProperty->isPropertyDelegateInitializedWithInitialValue()) + return false; + + auto delegateInfo = originalProperty->getPropertyDelegateBackingPropertyInfo(); + if (!delegateInfo || !delegateInfo.initializeFromOriginal) + return false; + + SILGenFunction::OpaqueValueRAII opaqueValue( + SGF, + delegateInfo.underlyingValue, + std::move(arg).getAsSingleValue(SGF, loc)); + + body(delegateInfo.initializeFromOriginal); + return true; +} + static void emitImplicitValueConstructor(SILGenFunction &SGF, ConstructorDecl *ctor) { RegularLocation Loc(ctor); @@ -138,7 +169,6 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, // If we have an indirect return slot, initialize it in-place. if (resultSlot) { - auto elti = elements.begin(), eltEnd = elements.end(); for (VarDecl *field : decl->getStoredProperties()) { auto fieldTy = selfTy.getFieldType(field, SGF.SGM.M); @@ -164,7 +194,14 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, assert(elti != eltEnd && "number of args does not match number of fields"); (void)eltEnd; - std::move(*elti).forwardInto(SGF, Loc, init.get()); + FullExpr scope(SGF.Cleanups, field->getParentPatternBinding()); + if (!maybeEmitPropertyDelegateInitFromValue( + SGF, Loc, field, std::move(*elti), + [&](Expr *expr) { + SGF.emitExprInto(expr, init.get()); + })) { + std::move(*elti).forwardInto(SGF, Loc, init.get()); + } ++elti; } SGF.B.createReturn(ImplicitReturnLocation::getImplicitReturnLoc(Loc), @@ -188,9 +225,17 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, v = SGF.emitRValue(field->getParentInitializer()) .forwardAsSingleStorageValue(SGF, fieldTy, Loc); } else { + FullExpr scope(SGF.Cleanups, field->getParentPatternBinding()); assert(elti != eltEnd && "number of args does not match number of fields"); (void)eltEnd; - v = std::move(*elti).forwardAsSingleStorageValue(SGF, fieldTy, Loc); + if (!maybeEmitPropertyDelegateInitFromValue( + SGF, Loc, field, std::move(*elti), + [&](Expr *expr) { + v = SGF.emitRValue(expr) + .forwardAsSingleStorageValue(SGF, fieldTy, Loc); + })) { + v = std::move(*elti).forwardAsSingleStorageValue(SGF, fieldTy, Loc); + } ++elti; } @@ -871,6 +916,17 @@ static Type getInitializationTypeInContext( DeclContext *fromDC, DeclContext *toDC, Pattern *pattern) { auto interfaceType = pattern->getType()->mapTypeOutOfContext(); + + // If this pattern is initializing the backing storage for a property + // with an attached delegate that is initialized with `=`, the + // initialization type is the original property type. + if (auto singleVar = pattern->getSingleVar()) { + if (auto originalProperty = singleVar->getOriginalDelegatedProperty()) { + if (originalProperty->isPropertyDelegateInitializedWithInitialValue()) + interfaceType = originalProperty->getValueInterfaceType(); + } + } + auto resultType = toDC->mapTypeIntoContext(interfaceType); return resultType; @@ -885,7 +941,7 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc, if (pbd->isStatic()) continue; for (auto entry : pbd->getPatternList()) { - auto init = entry.getNonLazyInit(); + auto init = entry.getExecutableInit(); if (!init) continue; // Cleanup after this initialization. @@ -928,6 +984,17 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc, resultType, origResultType, SGFContext()); + // If we have the backing storage for a property with an attached + // property delegate initialized with `=`, inject the value into an + // instance of the delegate. + if (auto singleVar = pbd->getSingleVar()) { + (void)maybeEmitPropertyDelegateInitFromValue( + *this, init, singleVar, std::move(result), + [&](Expr *expr) { + result = emitRValue(expr); + }); + } + emitMemberInit(*this, selfDecl, entry.getPattern(), std::move(result)); } } diff --git a/lib/SILGen/SILGenDecl.cpp b/lib/SILGen/SILGenDecl.cpp index 0c2e353e17df0..8198ca4ae917c 100644 --- a/lib/SILGen/SILGenDecl.cpp +++ b/lib/SILGen/SILGenDecl.cpp @@ -1173,7 +1173,7 @@ void SILGenFunction::emitPatternBinding(PatternBindingDecl *PBD, // If an initial value expression was specified by the decl, emit it into // the initialization. Otherwise, mark it uninitialized for DI to resolve. - if (auto *Init = entry.getNonLazyInit()) { + if (auto *Init = entry.getExecutableInit()) { FullExpr Scope(Cleanups, CleanupLocation(Init)); emitExprInto(Init, initialization.get(), SILLocation(PBD)); } else { diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 855ff8adfe05b..da9ddf2270f0f 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -20,6 +20,7 @@ #include "SILGenFunctionBuilder.h" #include "Scope.h" #include "swift/AST/Initializer.h" +#include "swift/AST/PropertyDelegates.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILProfiler.h" #include "swift/SIL/SILUndef.h" @@ -198,6 +199,13 @@ void SILGenFunction::emitCaptures(SILLocation loc, continue; } + if (capture.isOpaqueValue()) { + OpaqueValueExpr *opaqueValue = capture.getOpaqueValue(); + capturedArgs.push_back( + emitRValueAsSingleValue(opaqueValue).ensurePlusOne(*this, loc)); + continue; + } + auto *vd = capture.getDecl(); // FIXME: Expansion @@ -651,14 +659,27 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, VarDecl *var) { auto decl = function.getAbstractFunctionDecl(); auto *dc = decl->getInnermostDeclContext(); auto interfaceType = var->getValueInterfaceType(); + auto varType = var->getType(); + + // If this is the backing storage for a property with an attached + // delegate that was initialized with '=', the stored property initializer + // will be in terms of the original property's type. + if (auto originalProperty = var->getOriginalDelegatedProperty()) { + if (originalProperty->isPropertyDelegateInitializedWithInitialValue()) { + interfaceType = originalProperty->getValueInterfaceType(); + varType = originalProperty->getType(); + } + } + emitProlog(/*paramList*/ nullptr, /*selfParam*/ nullptr, interfaceType, dc, false); - prepareEpilog(var->getType(), false, CleanupLocation::get(loc)); + prepareEpilog(varType, false, CleanupLocation::get(loc)); auto pbd = var->getParentPatternBinding(); auto entry = pbd->getPatternEntryForVarDecl(var); auto subs = getForwardingSubstitutionMap(); - auto resultType = decl->mapTypeIntoContext(interfaceType)->getCanonicalType(); + auto contextualType = dc->mapTypeIntoContext(interfaceType); + auto resultType = contextualType->getCanonicalType(); auto origResultType = AbstractionPattern(resultType); SmallVector directResults; diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index ac8902b1c267c..78bf800dca9d6 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -1471,6 +1471,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction PreparedArguments &&args, Type overriddenSelfType, SGFContext ctx); + RValue emitApplyPropertyDelegateAllocator(SILLocation loc, + SubstitutionMap subs, + SILDeclRef ctorRef, + Type delegateTy, + CanAnyFunctionType funcTy); + CleanupHandle emitBeginApply(SILLocation loc, ManagedValue fn, SubstitutionMap subs, ArrayRef args, CanSILFunctionType substFnType, diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 17cbbf4143edf..68e7078d48077 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -26,6 +26,8 @@ #include "swift/AST/DiagnosticsCommon.h" #include "swift/AST/DiagnosticsSIL.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/PropertyDelegates.h" +#include "swift/AST/TypeCheckRequests.h" #include "swift/SIL/InstructionUtils.h" #include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/PrettyStackTrace.h" @@ -1291,6 +1293,32 @@ namespace { { } + bool hasPropertyDelegate() const { + if (auto *VD = dyn_cast(Storage)) { + if (auto delegateInfo = VD->getAttachedPropertyDelegateTypeInfo()) { + // If there is no init(initialValue:), we cannot rewrite an + // assignment into an initialization. + if (!delegateInfo.initialValueInit) + return false; + + // If we have a nonmutating setter on a value type, the call + // captures all of 'self' and we cannot rewrite an assignment + // into an initialization. + auto setter = VD->getSetter(); + if (setter->isNonMutating() && + setter->getDeclContext()->getSelfNominalTypeDecl() && + setter->isInstanceMember() && + !setter->getDeclContext()->getDeclaredInterfaceType() + ->hasReferenceSemantics()) { + return false; + } + + return true; + } + } + return false; + } + void emitAssignWithSetter(SILGenFunction &SGF, SILLocation loc, LValue &&dest, ArgumentSource &&value) { assert(getAccessorDecl()->isSetter()); @@ -1344,6 +1372,86 @@ namespace { assert(getAccessorDecl()->isSetter()); SILDeclRef setter = Accessor; + if (hasPropertyDelegate() && IsOnSelfParameter) { + // This is delegated property. Instead of emitting a setter, emit an + // assign_by_delegate with the allocating initializer function and the + // setter function as arguments. DefiniteInitializtion will then decide + // between the two functions, depending if it's an initialization or a + // re-assignment. + // + VarDecl *field = dyn_cast(Storage); + VarDecl *backingVar = field->getPropertyDelegateBackingProperty(); + assert(backingVar); + CanType ValType = backingVar->getType()->getCanonicalType(); + SILType varStorageType = + SGF.SGM.Types.getSubstitutedStorageType(backingVar, ValType); + auto typeData = + getLogicalStorageTypeData(SGF.SGM, getTypeData().AccessKind, ValType); + + // Get the address of the storage property. + ManagedValue proj; + if (BaseFormalType->mayHaveSuperclass()) { + RefElementComponent REC(backingVar, LValueOptions(), varStorageType, + typeData); + proj = std::move(REC).project(SGF, loc, base); + } else { + assert(BaseFormalType->getStructOrBoundGenericStruct()); + StructElementComponent SEC(backingVar, varStorageType, typeData); + proj = std::move(SEC).project(SGF, loc, base); + } + + // Create the allocating initializer function. It captures the metadata. + auto delegateInfo = field->getAttachedPropertyDelegateTypeInfo(); + auto ctor = delegateInfo.initialValueInit; + SubstitutionMap subs = backingVar->getType()->getMemberSubstitutionMap( + SGF.getModule().getSwiftModule(), ctor); + + Type ity = ctor->getInterfaceType(); + AnyFunctionType *substIty = ity.subst(subs)->castTo(); + + auto initRef = SILDeclRef(ctor, SILDeclRef::Kind::Allocator) + .asForeign(requiresForeignEntryPoint(ctor)); + RValue initFuncRV = + SGF.emitApplyPropertyDelegateAllocator(loc, subs,initRef, + backingVar->getType(), + CanAnyFunctionType(substIty)); + ManagedValue initFn = std::move(initFuncRV).getAsSingleValue(SGF, loc); + + // Create the allocating setter function. It captures the base address. + auto setterInfo = SGF.getConstantInfo(setter); + SILValue setterFRef = SGF.emitGlobalFunctionRef(loc, setter, setterInfo); + auto setterSubs = SGF.getFunction().getForwardingSubstitutionMap(); + + SILType closureTy = SILGenBuilder::getPartialApplyResultType( + setterFRef->getType(), 1, SGF.SGM.M, setterSubs, + ParameterConvention::Direct_Guaranteed); + + CanSILFunctionType setterTy = setterFRef->getType().castTo(); + SILFunctionConventions setterConv(setterTy, SGF.SGM.M); + + SILValue capturedBase; + unsigned argIdx = setterConv.getNumSILArguments() - 1; + if (setterConv.getSILArgumentConvention(argIdx).isInoutConvention()) { + capturedBase = base.getValue(); + } else { + capturedBase = base.copy(SGF, loc).forward(SGF); + } + + PartialApplyInst *setterPAI = + SGF.B.createPartialApply(loc, setterFRef, setterFRef->getType(), + setterSubs, { capturedBase }, closureTy); + ManagedValue setterFn = SGF.emitManagedRValueWithCleanup(setterPAI); + + // Create the assign_by_delegate with the allocator and setter. + assert(value.isRValue()); + ManagedValue Mval = std::move(value).asKnownRValue(SGF). + getAsSingleValue(SGF, loc); + SGF.B.createAssignByDelegate(loc, Mval.forward(SGF), proj.forward(SGF), + initFn.getValue(), setterFn.getValue(), + AssignOwnershipQualifier::Unknown); + return; + } + FormalEvaluationScope scope(SGF); // Pass in just the setter. auto args = @@ -4126,6 +4234,8 @@ static bool trySetterPeephole(SILGenFunction &SGF, SILLocation loc, } auto &setterComponent = static_cast(component); + if (setterComponent.hasPropertyDelegate()) + return false; setterComponent.emitAssignWithSetter(SGF, loc, std::move(dest), std::move(src)); return true;; diff --git a/lib/SILGen/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index 8145c4d8a79c0..0a84513d3d34d 100644 --- a/lib/SILGen/SILGenProlog.cpp +++ b/lib/SILGen/SILGenProlog.cpp @@ -455,7 +455,24 @@ void SILGenFunction::emitProlog(AnyFunctionRef TheClosure, SILValue val = F.begin()->createFunctionArgument(ty); (void) val; - return; + continue; + } + + if (capture.isOpaqueValue()) { + OpaqueValueExpr *opaqueValue = capture.getOpaqueValue(); + Type type = opaqueValue->getType()->mapTypeOutOfContext(); + type = GenericEnvironment::mapTypeIntoContext( + TheClosure.getGenericEnvironment(), type); + auto &lowering = getTypeLowering(type); + SILType ty = lowering.getLoweredType(); + SILValue val = F.begin()->createFunctionArgument(ty); + OpaqueValues[opaqueValue] = ManagedValue::forUnmanaged(val); + + // Opaque values are always passed 'owned', so add a clean up if needed. + if (!lowering.isTrivial()) + enterDestroyCleanup(val); + + continue; } emitCaptureArguments(*this, TheClosure, capture, ++ArgNo); diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index c9b8f142a9160..9db3c537c57ec 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -1024,7 +1024,7 @@ class SILGenType : public TypeMemberVisitor { void visitPatternBindingDecl(PatternBindingDecl *pd) { // Emit initializers. for (unsigned i = 0, e = pd->getNumPatternEntries(); i != e; ++i) { - if (pd->getNonLazyInit(i)) { + if (pd->getExecutableInit(i)) { if (pd->isStatic()) SGM.emitGlobalInitialization(pd, i); else @@ -1142,7 +1142,7 @@ class SILGenExtension : public TypeMemberVisitor { void visitPatternBindingDecl(PatternBindingDecl *pd) { // Emit initializers for static variables. for (unsigned i = 0, e = pd->getNumPatternEntries(); i != e; ++i) { - if (pd->getNonLazyInit(i)) { + if (pd->getExecutableInit(i)) { assert(pd->isStatic() && "stored property in extension?!"); SGM.emitGlobalInitialization(pd, i); } diff --git a/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp b/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp index 9b356e186d136..877a68eeea832 100644 --- a/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp +++ b/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp @@ -36,6 +36,7 @@ #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILUndef.h" +#include "swift/SIL/InstructionUtils.h" #include "swift/SILOptimizer/Analysis/ClosureScope.h" #include "swift/SILOptimizer/Analysis/PostOrderAnalysis.h" #include "swift/SILOptimizer/PassManager/Transforms.h" @@ -255,7 +256,8 @@ static void checkUsesOfAccess(BeginAccessInst *access) { for (auto *use : access->getUses()) { auto user = use->getUser(); assert(!isa(user)); - assert(!isa(user)); + assert(!isa(user) || + onlyUsedByAssignByDelegate(cast(user))); } #endif } diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index 4f75d2497c893..f86629e50d75a 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -356,7 +356,12 @@ DIMemoryObjectInfo::getPathStringToElement(unsigned Element, getElementCountRec(Module, FieldType, false); if (Element < NumFieldElements) { Result += '.'; - Result += VD->getName().str(); + auto originalProperty = VD->getOriginalDelegatedProperty(); + if (originalProperty) { + Result += originalProperty->getName().str(); + } else { + Result += VD->getName().str(); + } getPathStringToElementRec(Module, FieldType, Element, Result); return VD; } @@ -415,9 +420,14 @@ bool DIMemoryObjectInfo::isElementLetProperty(unsigned Element) const { //===----------------------------------------------------------------------===// /// onlyTouchesTrivialElements - Return true if all of the accessed elements -/// have trivial type. +/// have trivial type and the access itself is a trivial instruction. bool DIMemoryUse::onlyTouchesTrivialElements( const DIMemoryObjectInfo &MI) const { + // assign_by_delegate calls functions to assign a value. This is not + // considered as trivial. + if (isa(Inst)) + return false; + auto *F = Inst->getFunction(); for (unsigned i = FirstElement, e = i + NumElements; i != e; ++i) { @@ -741,9 +751,12 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { #include "swift/AST/ReferenceStorage.def" // Stores *to* the allocation are writes. - if ((isa(User) || isa(User)) && + if ((isa(User) || isa(User) || + isa(User)) && Op->getOperandNumber() == 1) { if (PointeeType.is()) { + assert(!isa(User) && + "cannot assign a typle with assign_by_delegate"); UsesToScalarize.push_back(User); continue; } @@ -753,7 +766,7 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { DIUseKind Kind; if (InStructSubElement) Kind = DIUseKind::PartialStore; - else if (isa(User)) + else if (isa(User) || isa(User)) Kind = DIUseKind::InitOrAssign; else if (PointeeType.isTrivial(*User->getFunction())) Kind = DIUseKind::InitOrAssign; @@ -990,6 +1003,11 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { continue; } + if (auto *PAI = dyn_cast(User)) { + if (onlyUsedByAssignByDelegate(PAI)) + continue; + } + // Sanitizer instrumentation is not user visible, so it should not // count as a use and must not affect compile-time diagnostics. if (isSanitizerInstrumentation(User)) @@ -1466,8 +1484,11 @@ void ElementUseCollector::collectClassSelfUses( // If this is a partial application of self, then this is an escape point // for it. - if (isa(User)) + if (auto *PAI = dyn_cast(User)) { + if (onlyUsedByAssignByDelegate(PAI)) + continue; Kind = DIUseKind::Escape; + } trackUse(DIMemoryUse(User, Kind, 0, TheMemory.NumElements)); } diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h index a11f655a7fb05..cc9b8c56add95 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h @@ -267,7 +267,7 @@ struct DIMemoryUse { } /// onlyTouchesTrivialElements - Return true if all of the accessed elements - /// have trivial type. + /// have trivial type and the access itself is a trivial instruction. bool onlyTouchesTrivialElements(const DIMemoryObjectInfo &MemoryInfo) const; /// getElementBitmask - Return a bitmask with the touched tuple elements diff --git a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp index 5037766b7ab60..85203f9de0ec9 100644 --- a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp +++ b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp @@ -979,6 +979,8 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) { addr = copyAddr->getDest(); else if (auto *assign = dyn_cast(inst)) addr = assign->getDest(); + else if (auto *assign = dyn_cast(inst)) + addr = assign->getDest(); else return false; @@ -1823,7 +1825,7 @@ void LifetimeChecker::handleSelfInitUse(unsigned UseID) { assert(TheMemory.NumElements == 1 && "delegating inits have a single elt"); // Lower Assign instructions if needed. - if (isa(Use.Inst)) + if (isa(Use.Inst) || isa(Use.Inst)) NeedsUpdateForInitState.push_back(UseID); } else { // super.init also requires that all ivars are initialized before the @@ -1899,6 +1901,24 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) { return; } + if (auto *AI = dyn_cast(Inst)) { + // Remove this instruction from our data structures, since we will be + // removing it. + Use.Inst = nullptr; + NonLoadUses.erase(Inst); + + if (TheMemory.isClassInitSelf() && + Use.Kind == DIUseKind::SelfInit) { + assert(InitKind == IsInitialization); + AI->setOwnershipQualifier(AssignOwnershipQualifier::Reinit); + } else { + AI->setOwnershipQualifier((InitKind == IsInitialization + ? AssignOwnershipQualifier::Init + : AssignOwnershipQualifier::Reassign)); + } + + return; + } // Ignore non-stores for SelfInits. assert(isa(Inst) && "Unknown store instruction!"); diff --git a/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp index ab0a5979f4ff4..5c7ce810bb712 100644 --- a/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp +++ b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp @@ -91,12 +91,104 @@ static void lowerAssignInstruction(SILBuilderWithScope &b, AssignInst *inst) { inst->eraseFromParent(); } +static void lowerAssignByDelegateInstruction(SILBuilderWithScope &b, + AssignByDelegateInst *inst, + SmallVectorImpl &accessMarkers) { + LLVM_DEBUG(llvm::dbgs() << " *** Lowering [isInit=" + << unsigned(inst->getOwnershipQualifier()) + << "]: " << *inst << "\n"); + + ++numAssignRewritten; + + SILValue src = inst->getSrc(); + SILValue dest = inst->getDest(); + SILLocation loc = inst->getLoc(); + SILArgumentConvention srcConvention(SILArgumentConvention::Indirect_Inout); + + switch (inst->getOwnershipQualifier()) { + case AssignOwnershipQualifier::Init: { + SILValue initFn = inst->getInitializer(); + CanSILFunctionType fTy = initFn->getType().castTo(); + SILFunctionConventions convention(fTy, inst->getModule()); + if (convention.hasIndirectSILResults()) { + b.createApply(loc, initFn, { dest, src }, false); + srcConvention = convention.getSILArgumentConvention(1); + } else { + SILValue wrappedSrc = b.createApply(loc, initFn, { src }, false); + b.createTrivialStoreOr(loc, wrappedSrc, dest, StoreOwnershipQualifier::Init); + srcConvention = convention.getSILArgumentConvention(0); + } + // TODO: remove the unused setter function, which usually is a dead + // partial_apply. + break; + } + case AssignOwnershipQualifier::Unknown: + case AssignOwnershipQualifier::Reassign: { + SILValue setterFn = inst->getSetter(); + CanSILFunctionType fTy = setterFn->getType().castTo(); + SILFunctionConventions convention(fTy, inst->getModule()); + assert(!convention.hasIndirectSILResults()); + srcConvention = convention.getSILArgumentConvention(0); + b.createApply(loc, setterFn, { src }, false); + + // The destination address is not used. Remove it if it is a dead access + // marker. This is important, because also the setter function contains + // access marker. In case those markers are dynamic it would cause a + // nested access violation. + if (auto *BA = dyn_cast(dest)) + accessMarkers.push_back(BA); + // TODO: remove the unused init function, which usually is a dead + // partial_apply. + break; + } + case AssignOwnershipQualifier::Reinit: + llvm_unreachable("wrong qualifier for assign_by_delegate"); + } + switch (srcConvention) { + case SILArgumentConvention::Indirect_In_Guaranteed: + b.createDestroyAddr(loc, src); + break; + case SILArgumentConvention::Direct_Guaranteed: + b.createDestroyValue(loc, src); + break; + case SILArgumentConvention::Direct_Unowned: + case SILArgumentConvention::Indirect_In: + case SILArgumentConvention::Indirect_In_Constant: + case SILArgumentConvention::Direct_Owned: + break; + case SILArgumentConvention::Indirect_Inout: + case SILArgumentConvention::Indirect_InoutAliasable: + case SILArgumentConvention::Indirect_Out: + case SILArgumentConvention::Direct_Deallocating: + llvm_unreachable("wrong convention for setter/initializer src argument"); + } + inst->eraseFromParent(); +} + +static void deleteDeadAccessMarker(BeginAccessInst *BA) { + SmallVector Users; + for (Operand *Op : BA->getUses()) { + SILInstruction *User = Op->getUser(); + if (!isa(User)) + return; + + Users.push_back(User); + } + for (SILInstruction *User: Users) { + User->eraseFromParent(); + } + BA->eraseFromParent(); +} + /// lowerRawSILOperations - There are a variety of raw-sil instructions like /// 'assign' that are only used by this pass. Now that definite initialization /// checking is done, remove them. static bool lowerRawSILOperations(SILFunction &fn) { bool changed = false; + for (auto &bb : fn) { + SmallVector accessMarkers; + auto i = bb.begin(), e = bb.end(); while (i != e) { SILInstruction *inst = &*i; @@ -123,6 +215,13 @@ static bool lowerRawSILOperations(SILFunction &fn) { continue; } + if (auto *ai = dyn_cast(inst)) { + SILBuilderWithScope b(ai); + lowerAssignByDelegateInstruction(b, ai, accessMarkers); + changed = true; + continue; + } + // mark_uninitialized just becomes a noop, resolving to its operand. if (auto *mui = dyn_cast(inst)) { mui->replaceAllUsesWith(mui->getOperand()); @@ -138,6 +237,9 @@ static bool lowerRawSILOperations(SILFunction &fn) { continue; } } + for (BeginAccessInst *BA : accessMarkers) { + deleteDeadAccessMarker(BA); + } } return changed; } diff --git a/lib/SILOptimizer/Utils/SILInliner.cpp b/lib/SILOptimizer/Utils/SILInliner.cpp index a91ac5a74fe41..bc618895bd945 100644 --- a/lib/SILOptimizer/Utils/SILInliner.cpp +++ b/lib/SILOptimizer/Utils/SILInliner.cpp @@ -731,6 +731,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::ValueMetatypeInst: case SILInstructionKind::WitnessMethodInst: case SILInstructionKind::AssignInst: + case SILInstructionKind::AssignByDelegateInst: case SILInstructionKind::BranchInst: case SILInstructionKind::CheckedCastBranchInst: case SILInstructionKind::CheckedCastValueBranchInst: diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt index 15c1cea16b6cc..dec2f5aae1b29 100644 --- a/lib/Sema/CMakeLists.txt +++ b/lib/Sema/CMakeLists.txt @@ -51,6 +51,7 @@ add_swift_host_library(swiftSema STATIC TypeCheckGeneric.cpp TypeCheckNameLookup.cpp TypeCheckPattern.cpp + TypeCheckPropertyDelegate.cpp TypeCheckProtocol.cpp TypeCheckProtocolInference.cpp TypeCheckREPL.cpp diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 59c9c4c355078..709031830584d 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -3189,7 +3189,8 @@ namespace { } Expr *visitOpaqueValueExpr(OpaqueValueExpr *expr) { - llvm_unreachable("Already type-checked"); + assert(expr->isPlaceholder() && "Already type-checked"); + return expr; } Expr *visitDefaultArgumentExpr(DefaultArgumentExpr *expr) { diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 09d817a10c1e9..d15b9785fbbdd 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2100,9 +2100,12 @@ namespace { // will avoid exponential typecheck behavior in the case of // tuples, nested arrays, and dictionary literals. // + // FIXME: This should be handled in the solver, not here. + // // Otherwise, create a new type variable. auto ty = Type(); - if (!var->hasNonPatternBindingInit()) { + if (!var->hasNonPatternBindingInit() && + !var->getAttachedPropertyDelegate()) { if (auto boundExpr = locator.trySimplifyToExpr()) { if (!boundExpr->isSemanticallyInOutExpr()) ty = CS.getType(boundExpr)->getRValueType(); @@ -2487,7 +2490,8 @@ namespace { } Type visitOpaqueValueExpr(OpaqueValueExpr *expr) { - llvm_unreachable("Already type checked"); + assert(expr->isPlaceholder() && "Already type checked"); + return expr->getType(); } Type visitDefaultArgumentExpr(DefaultArgumentExpr *expr) { @@ -3285,7 +3289,7 @@ namespace { expr = value->second; continue; } else { - assert(eraseOpenExistentialsOnly && + assert((eraseOpenExistentialsOnly || OVE->isPlaceholder()) && "Didn't see this OVE in a containing OpenExistentialExpr?"); // NOTE: In 'eraseOpenExistentialsOnly' mode, ASTWalker may walk // into other kind of expressions holding OVE. diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 812098d89b44d..2e1da532a5a51 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -27,7 +27,9 @@ #include "swift/AST/GenericSignatureBuilder.h" #include "swift/AST/Initializer.h" #include "swift/AST/ParameterList.h" +#include "swift/AST/PropertyDelegates.h" #include "swift/AST/ProtocolConformance.h" +#include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Defer.h" #include "swift/ClangImporter/ClangModule.h" #include "llvm/ADT/SmallString.h" @@ -310,6 +312,22 @@ static void maybeMarkTransparent(AccessorDecl *accessor, ASTContext &ctx) { accessor->getAccessorKind() == AccessorKind::Set) return; + // Getters/setters for a property with a delegate are not @_transparent if + // the backing variable has more-restrictive access than the original + // property. The same goes for its storage delegate. + if (auto var = dyn_cast(accessor->getStorage())) { + if (auto backingVar = var->getPropertyDelegateBackingProperty()) { + if (backingVar->getFormalAccess() < var->getFormalAccess()) + return; + } + + if (auto original = var->getOriginalDelegatedProperty( + PropertyDelegateSynthesizedPropertyKind::StorageDelegate)) { + if (var->getFormalAccess() < original->getFormalAccess()) + return; + } + } + // Accessors for protocol storage requirements are never @_transparent // since they do not have bodies. // @@ -517,7 +535,13 @@ namespace { /// an override of it. Implementation, /// We're referencing the superclass's implementation of the storage. - Super + Super, + /// We're referencing the backing property for a property with a delegate + /// through the 'value' property. + Delegate, + /// We're referencing the backing property for a property with a delegate + /// through the 'storage' property. + DelegateStorage, }; } // end anonymous namespace @@ -526,6 +550,17 @@ static Expr *buildStorageReference(AccessorDecl *accessor, AbstractStorageDecl *storage, TargetImpl target, ASTContext &ctx) { + // Local function to "finish" the expression, creating a member reference + // to the underlying variable if there is one. + VarDecl *underlyingVar = nullptr; + auto finish = [&](Expr *result) -> Expr * { + if (!underlyingVar) + return result; + + return new (ctx) MemberRefExpr( + result, SourceLoc(), underlyingVar, DeclNameLoc(), /*Implicit=*/true); + }; + AccessSemantics semantics; SelfAccessorKind selfAccessKind; switch (target) { @@ -559,12 +594,33 @@ static Expr *buildStorageReference(AccessorDecl *accessor, selfAccessKind = SelfAccessorKind::Peer; } break; + + case TargetImpl::Delegate: { + auto var = cast(accessor->getStorage()); + storage = var->getPropertyDelegateBackingProperty(); + underlyingVar = var->getAttachedPropertyDelegateTypeInfo().valueVar; + semantics = AccessSemantics::DirectToStorage; + selfAccessKind = SelfAccessorKind::Peer; + break; + } + + case TargetImpl::DelegateStorage: { + auto var = + cast(accessor->getStorage())->getOriginalDelegatedProperty(); + storage = var->getPropertyDelegateBackingProperty(); + underlyingVar = var->getAttachedPropertyDelegateTypeInfo().delegateValueVar; + assert(underlyingVar); + semantics = AccessSemantics::DirectToStorage; + selfAccessKind = SelfAccessorKind::Peer; + break; + } } VarDecl *selfDecl = accessor->getImplicitSelfDecl(); if (!selfDecl) { assert(target != TargetImpl::Super); - return new (ctx) DeclRefExpr(storage, DeclNameLoc(), IsImplicit, semantics); + return finish( + new (ctx) DeclRefExpr(storage, DeclNameLoc(), IsImplicit, semantics)); } Expr *selfDRE = @@ -572,12 +628,12 @@ static Expr *buildStorageReference(AccessorDecl *accessor, if (auto subscript = dyn_cast(storage)) { Expr *indices = buildSubscriptIndexReference(ctx, accessor); - return SubscriptExpr::create(ctx, selfDRE, indices, storage, - IsImplicit, semantics); + return finish(SubscriptExpr::create(ctx, selfDRE, indices, storage, + IsImplicit, semantics)); } - return new (ctx) MemberRefExpr(selfDRE, SourceLoc(), storage, - DeclNameLoc(), IsImplicit, semantics); + return finish(new (ctx) MemberRefExpr(selfDRE, SourceLoc(), storage, + DeclNameLoc(), IsImplicit, semantics)); } /// Load the value of VD. If VD is an @override of another value, we call the @@ -727,7 +783,9 @@ void createPropertyStoreOrCallSuperclassSetter(AccessorDecl *accessor, LLVM_ATTRIBUTE_UNUSED static bool isSynthesizedComputedProperty(AbstractStorageDecl *storage) { return (storage->getAttrs().hasAttribute() || - storage->getAttrs().hasAttribute()); + storage->getAttrs().hasAttribute() || + (isa(storage) && + cast(storage)->getAttachedPropertyDelegate())); } /// Synthesize the body of a trivial getter. For a non-member vardecl or one @@ -738,8 +796,9 @@ static void synthesizeTrivialGetterBody(AccessorDecl *getter, TargetImpl target, ASTContext &ctx) { auto storage = getter->getStorage(); - assert(!storage->getAttrs().hasAttribute() && - !storage->getAttrs().hasAttribute()); + assert(!isSynthesizedComputedProperty(storage) || + target == TargetImpl::Delegate || + target == TargetImpl::DelegateStorage); SourceLoc loc = storage->getLoc(); @@ -787,6 +846,13 @@ static void synthesizeReadCoroutineGetterBody(AccessorDecl *getter, synthesizeTrivialGetterBody(getter, TargetImpl::Implementation, ctx); } +/// Synthesize the body of a getter for a property delegate, which +/// delegates to the delegate's unwrap property. +static void synthesizePropertyDelegateGetterBody(AccessorDecl *getter, + ASTContext &ctx) { + synthesizeTrivialGetterBody(getter, TargetImpl::Delegate, ctx); +} + /// Synthesize the body of a setter which just stores to the given storage /// declaration (which doesn't have to be the storage for the setter). static void @@ -817,6 +883,14 @@ static void synthesizeTrivialSetterBody(AccessorDecl *setter, storage, ctx); } +/// Synthesize the body of a setter for a property delegate, which +/// delegates to the delegate's unwrap property. +static void synthesizePropertyDelegateSetterBody(AccessorDecl *setter, + ASTContext &ctx) { + synthesizeTrivialSetterBodyWithStorage(setter, TargetImpl::Delegate, + setter->getStorage(), ctx); +} + static void synthesizeCoroutineAccessorBody(AccessorDecl *accessor, ASTContext &ctx) { assert(accessor->isCoroutine()); @@ -1275,7 +1349,7 @@ static void synthesizeLazyGetterBody(AbstractFunctionDecl *fn, void *context) { auto *InitValue = VD->getParentInitializer(); auto PBD = VD->getParentPatternBinding(); unsigned entryIndex = PBD->getPatternEntryIndexForVarDecl(VD); - assert(PBD->isInitializerLazy(entryIndex)); + assert(PBD->isInitializerSubsumed(entryIndex)); bool wasInitializerChecked = PBD->isInitializerChecked(entryIndex); PBD->setInitializerChecked(entryIndex); @@ -1381,8 +1455,212 @@ void swift::completeLazyVarImplementation(VarDecl *VD) { Storage->overwriteSetterAccess(AccessLevel::Private); } +/// Synthesize a computed property `$foo` for a property with an attached +/// delegate that has a `delegateValue` property. +static VarDecl *synthesizePropertyDelegateStorageDelegateProperty( + ASTContext &ctx, VarDecl *var, Type delegateType, + VarDecl *delegateVarVar) { + // Compute the name of the storage type. + SmallString<64> nameBuf; + nameBuf = "$"; + nameBuf += var->getName().str(); + Identifier name = ctx.getIdentifier(nameBuf); + + // Determine the type of the property. + Type propertyType = delegateType->getTypeOfMember( + var->getModuleContext(), delegateVarVar, + delegateVarVar->getValueInterfaceType()); + Type propertyInterfaceType = propertyType->mapTypeOutOfContext(); + + // Form the property. + auto dc = var->getDeclContext(); + VarDecl *property = new (ctx) VarDecl(/*IsStatic=*/var->isStatic(), + VarDecl::Specifier::Var, + /*IsCaptureList=*/false, + var->getLoc(), + name, dc); + property->setInterfaceType(propertyInterfaceType); + property->setType(propertyType); + property->setImplicit(); + property->setOriginalDelegatedProperty(var); + addMemberToContextIfNeeded(property, dc, var); + + // Create the pattern binding declaration for the property. + Pattern *pbdPattern = new (ctx) NamedPattern(property, /*implicit=*/true); + pbdPattern->setType(propertyType); + pbdPattern = TypedPattern::createImplicit(ctx, pbdPattern, propertyType); + auto pbd = PatternBindingDecl::createImplicit( + ctx, property->getCorrectStaticSpelling(), pbdPattern, + /*init*/nullptr, dc, SourceLoc()); + addMemberToContextIfNeeded(pbd, dc, var); + + // Determine the access level for the property. + AccessLevel access = + std::min(AccessLevel::Internal, var->getFormalAccess()); + property->overwriteAccess(access); + + // Determine setter access. + AccessLevel setterAccess = + std::min(AccessLevel::Internal, var->getSetterFormalAccess()); + property->overwriteSetterAccess(setterAccess); + + // Add the accessors we need. + bool hasSetter = delegateVarVar->isSettable(nullptr) && + delegateVarVar->isSetterAccessibleFrom(var->getInnermostDeclContext()); + addGetterToStorage(property, ctx); + if (hasSetter) { + addSetterToStorage(property, ctx); + property->overwriteImplInfo(StorageImplInfo::getMutableComputed()); + } else { + property->overwriteImplInfo(StorageImplInfo::getImmutableComputed()); + } + addExpectedOpaqueAccessorsToStorage(property, ctx); + + return property; +} + +llvm::Expected +PropertyDelegateBackingPropertyInfoRequest::evaluate(Evaluator &evaluator, + VarDecl *var) const { + // Determine the type of the backing property. + auto delegateType = var->getPropertyDelegateBackingPropertyType(); + if (!delegateType) + return PropertyDelegateBackingPropertyInfo(); + + auto delegateInfo = var->getAttachedPropertyDelegateTypeInfo(); + if (!delegateInfo) + return PropertyDelegateBackingPropertyInfo(); + + // Compute the name of the storage type. + ASTContext &ctx = var->getASTContext(); + SmallString<64> nameBuf; + if (delegateInfo.delegateValueVar) + nameBuf = "$$"; + else + nameBuf = "$"; + nameBuf += var->getName().str(); + Identifier name = ctx.getIdentifier(nameBuf); + + // Determine the type of the storage. + bool isInvalid = delegateType->hasError(); + auto dc = var->getDeclContext(); + Type storageInterfaceType = delegateType; + + Type storageType = + var->getDeclContext()->mapTypeIntoContext(storageInterfaceType); + if (!storageType) { + storageType = ErrorType::get(ctx); + isInvalid = true; + } + + // Create the backing storage property and note it in the cache. + VarDecl *backingVar = new (ctx) VarDecl(/*IsStatic=*/var->isStatic(), + VarDecl::Specifier::Var, + /*IsCaptureList=*/false, + var->getLoc(), + name, dc); + backingVar->setInterfaceType(storageInterfaceType); + backingVar->setType(storageType); + backingVar->setImplicit(); + if (isInvalid) + backingVar->setInvalid(); + backingVar->setOriginalDelegatedProperty(var); + addMemberToContextIfNeeded(backingVar, dc, var); + + // Create the pattern binding declaration for the backing property. + Pattern *pbdPattern = new (ctx) NamedPattern(backingVar, /*implicit=*/true); + pbdPattern->setType(storageType); + pbdPattern = TypedPattern::createImplicit(ctx, pbdPattern, storageType); + auto pbd = PatternBindingDecl::createImplicit( + ctx, backingVar->getCorrectStaticSpelling(), pbdPattern, + /*init*/nullptr, dc, SourceLoc()); + addMemberToContextIfNeeded(pbd, dc, var); + + // Take the initializer from the original property. + auto parentPBD = var->getParentPatternBinding(); + unsigned patternNumber = parentPBD->getPatternEntryIndexForVarDecl(var); + if (parentPBD->isInitialized(patternNumber) && + !parentPBD->isInitializerChecked(patternNumber)) { + auto &tc = *static_cast(ctx.getLazyResolver()); + tc.typeCheckPatternBinding(parentPBD, patternNumber); + } + + Expr *originalInitialValue = nullptr; + if (Expr *init = parentPBD->getInit(patternNumber)) { + pbd->setInit(0, init); + pbd->setInitializerChecked(0); + originalInitialValue = findOriginalPropertyDelegateInitialValue(var, init); + } + + // Mark the backing property as 'final'. There's no sensible way to override. + if (dc->getSelfClassDecl()) + makeFinal(ctx, backingVar); + + // When there is a `delegateValue`, lower the access of the + AccessLevel defaultAccess = + delegateInfo.delegateValueVar ? AccessLevel::Private + : AccessLevel::Internal; + + // Determine the access level for the backing property. + AccessLevel access = + std::min(defaultAccess, var->getFormalAccess()); + backingVar->overwriteAccess(access); + + // Determine setter access. + AccessLevel setterAccess = + std::min(defaultAccess, var->getSetterFormalAccess()); + backingVar->overwriteSetterAccess(setterAccess); + + // If there is a storage delegate property (delegateVar) in the delegate, + // synthesize a computed property for '$foo'. + VarDecl *storageVar = nullptr; + if (delegateInfo.delegateValueVar) { + storageVar = synthesizePropertyDelegateStorageDelegateProperty( + ctx, var, delegateType, delegateInfo.delegateValueVar); + } + + // Get the property delegate information. + if (!delegateInfo.initialValueInit) { + assert(!originalInitialValue); + return PropertyDelegateBackingPropertyInfo( + backingVar, storageVar, nullptr, nullptr, nullptr); + } + + // Form the initialization of the backing property from a value of the + // original property's type. + OpaqueValueExpr *origValue = + new (ctx) OpaqueValueExpr(var->getLoc(), var->getType(), + /*isPlaceholder=*/true); + auto typeExpr = TypeExpr::createImplicit(storageType, ctx); + Expr *initializer = + CallExpr::createImplicit(ctx, typeExpr, {origValue}, + {ctx.Id_initialValue}); + + // Type-check the initialization. + auto &tc = *static_cast(ctx.getLazyResolver()); + tc.typeCheckExpression(initializer, dc); + if (auto initializerContext = + dyn_cast_or_null( + pbd->getPatternEntryForVarDecl(backingVar).getInitContext())) { + tc.contextualizeInitializer(initializerContext, initializer); + } + tc.checkPropertyDelegateErrorHandling(parentPBD, initializer); + return PropertyDelegateBackingPropertyInfo( + backingVar, storageVar, originalInitialValue, initializer, origValue); +} + static bool wouldBeCircularSynthesis(AbstractStorageDecl *storage, AccessorKind kind) { + // All property delegate accessors are non-circular. + if (auto var = dyn_cast(storage)) { + if (var->getAttachedPropertyDelegate()) + return false; + + if (var->getOriginalDelegatedProperty( + PropertyDelegateSynthesizedPropertyKind::StorageDelegate)) + return false; + } + switch (kind) { case AccessorKind::Get: return storage->getReadImpl() == ReadImplKind::Get; @@ -1433,10 +1711,12 @@ void swift::triggerAccessorSynthesis(TypeChecker &TC, if (!accessor) return; - accessor->setBodySynthesizer(&synthesizeAccessorBody); + if (!accessor->hasBody()) { + accessor->setBodySynthesizer(&synthesizeAccessorBody); - TC.Context.addSynthesizedDecl(accessor); - TC.DeclsToFinalize.insert(accessor); + TC.Context.addSynthesizedDecl(accessor); + TC.DeclsToFinalize.insert(accessor); + } }); } @@ -1458,6 +1738,37 @@ static void maybeAddAccessorsToLazyVariable(VarDecl *var, ASTContext &ctx) { addExpectedOpaqueAccessorsToStorage(var, ctx); } +static void maybeAddAccessorsForPropertyDelegate(VarDecl *var, + ASTContext &ctx) { + auto backingVar = var->getPropertyDelegateBackingProperty(); + if (!backingVar || backingVar->isInvalid()) + return; + + auto valueVar = var->getAttachedPropertyDelegateTypeInfo().valueVar; + assert(valueVar && "Cannot fail when the backing var is valid"); + + bool delegateSetterIsUsable = + var->getSetter() || + (!var->isLet() && + valueVar->isSettable(nullptr) && + valueVar->isSetterAccessibleFrom(var->getInnermostDeclContext())); + + if (!var->getGetter()) { + addGetterToStorage(var, ctx); + } + + if (delegateSetterIsUsable) + var->overwriteImplInfo(StorageImplInfo::getMutableComputed()); + else + var->overwriteImplInfo(StorageImplInfo::getImmutableComputed()); + + if (!var->getSetter() && delegateSetterIsUsable) { + addSetterToStorage(var, ctx); + } + + addExpectedOpaqueAccessorsToStorage(var, ctx); +} + /// Try to add the appropriate accessors required a storage declaration. /// This needs to be idempotent. /// @@ -1473,6 +1784,14 @@ void swift::maybeAddAccessorsToStorage(AbstractStorageDecl *storage) { return; } + // Property delegates require backing storage. + if (auto var = dyn_cast(storage)) { + if (var->getAttachedPropertyDelegate()) { + maybeAddAccessorsForPropertyDelegate(var, ctx); + return; + } + } + auto *dc = storage->getDeclContext(); // Local variables don't otherwise get accessors. @@ -1550,6 +1869,22 @@ void swift::maybeAddAccessorsToStorage(AbstractStorageDecl *storage) { static void synthesizeGetterBody(AccessorDecl *getter, ASTContext &ctx) { + auto storage = getter->getStorage(); + + // Synthesize the getter for a property delegate. + if (auto var = dyn_cast(storage)) { + if (var->getAttachedPropertyDelegate()) { + synthesizePropertyDelegateGetterBody(getter, ctx); + return; + } + + if (var->getOriginalDelegatedProperty( + PropertyDelegateSynthesizedPropertyKind::StorageDelegate)) { + synthesizeTrivialGetterBody(getter, TargetImpl::DelegateStorage, ctx); + return; + } + } + if (getter->hasForcedStaticDispatch()) { synthesizeTrivialGetterBody(getter, TargetImpl::Ordinary, ctx); return; @@ -1580,7 +1915,34 @@ static void synthesizeGetterBody(AccessorDecl *getter, static void synthesizeSetterBody(AccessorDecl *setter, ASTContext &ctx) { - switch (setter->getStorage()->getWriteImpl()) { + auto storage = setter->getStorage(); + + // Synthesize the setter for a property delegate. + if (auto var = dyn_cast(storage)) { + if (var->getAttachedPropertyDelegate()) { + if (var->getAccessor(AccessorKind::WillSet) || + var->getAccessor(AccessorKind::DidSet)) { + synthesizeObservedSetterBody(setter, TargetImpl::Delegate, ctx); + return; + } + + synthesizePropertyDelegateSetterBody(setter, ctx); + return; + } + + // Synthesize a getter for the storage delegate property of a property + // with an attached delegate. + if (auto original = var->getOriginalDelegatedProperty( + PropertyDelegateSynthesizedPropertyKind::StorageDelegate)) { + auto backingVar = original->getPropertyDelegateBackingProperty(); + synthesizeTrivialSetterBodyWithStorage(setter, + TargetImpl::DelegateStorage, + backingVar, ctx); + return; + } + } + + switch (storage->getWriteImpl()) { case WriteImplKind::Immutable: llvm_unreachable("synthesizing setter from immutable storage"); @@ -1672,15 +2034,19 @@ static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var, // the type will always be a sugared T? because we don't default init an // explicit Optional. if ((isa(var->getValueInterfaceType().getPointer()) && - !var->getParentInitializer()) || + !var->isParentInitialized()) || var->getAttrs().hasAttribute()) { arg->setDefaultArgumentKind(DefaultArgumentKind::NilLiteral); return; } + // If there's a backing storage property, the memberwise initializer + // will be in terms of that. + VarDecl *backingStorageVar = var->getPropertyDelegateBackingProperty(); + // Set the default value to the variable. When we emit this in silgen // we're going to call the variable's initializer expression. - arg->setStoredProperty(var); + arg->setStoredProperty(backingStorageVar ? backingStorageVar : var); arg->setDefaultArgumentKind(DefaultArgumentKind::StoredProperty); } @@ -1692,13 +2058,14 @@ bool swift::isMemberwiseInitialized(VarDecl *var) { if (var->isImplicit() || var->isStatic()) return false; - if (!var->hasStorage() && !var->getAttrs().hasAttribute()) + if (!var->hasStorage() && !var->getAttrs().hasAttribute() && + !var->getAttachedPropertyDelegate()) return false; // Initialized 'let' properties have storage, but don't get an argument // to the memberwise initializer since they already have an initial // value that cannot be overridden. - if (var->isLet() && var->getParentInitializer()) + if (var->isLet() && var->isParentInitialized()) return false; return true; @@ -1738,13 +2105,24 @@ ConstructorDecl *swift::createImplicitConstructor(TypeChecker &tc, tc.validateDecl(var); auto varInterfaceType = var->getValueInterfaceType(); - // If var is a lazy property, its value is provided for the underlying - // storage. We thus take an optional of the properties type. We only - // need to do this because the implicit constructor is added before all - // the properties are type checked. Perhaps init() synth should be moved - // later. - if (var->getAttrs().hasAttribute()) + if (var->getAttrs().hasAttribute()) { + // If var is a lazy property, its value is provided for the underlying + // storage. We thus take an optional of the property's type. We only + // need to do this because the implicit initializer is added before all + // the properties are type checked. Perhaps init() synth should be + // moved later. varInterfaceType = OptionalType::get(varInterfaceType); + } else if (Type backingPropertyType = + var->getPropertyDelegateBackingPropertyType()) { + // For a property that has a delegate, writing the initializer + // with an '=' implies that the memberwise initializer should also + // accept a value of the original property type. Otherwise, the + // memberwise initializer will be in terms of the backing storage + // type. + if (!var->isPropertyDelegateInitializedWithInitialValue()) { + varInterfaceType = backingPropertyType; + } + } // Create the parameter. auto *arg = new (ctx) diff --git a/lib/Sema/DebuggerTestingTransform.cpp b/lib/Sema/DebuggerTestingTransform.cpp index 9a091a2ea01ad..a3ac3752cec57 100644 --- a/lib/Sema/DebuggerTestingTransform.cpp +++ b/lib/Sema/DebuggerTestingTransform.cpp @@ -177,7 +177,7 @@ class DebuggerTestingTransform : public ASTWalker { // Don't capture variables which aren't default-initialized. if (auto *VD = dyn_cast(DstDecl)) - if (!VD->getParentInitializer() && !VD->isInOut()) + if (!VD->isParentInitialized() && !VD->isInOut()) return {true, OriginalExpr}; // Rewrite the original expression into this: diff --git a/lib/Sema/DerivedConformanceCodable.cpp b/lib/Sema/DerivedConformanceCodable.cpp index 049efec3afc6a..525892e9db3f1 100644 --- a/lib/Sema/DerivedConformanceCodable.cpp +++ b/lib/Sema/DerivedConformanceCodable.cpp @@ -223,7 +223,7 @@ static bool validateCodingKeysEnum(DerivedConformance &derived, continue; } - if (varDecl->getParentInitializer()) + if (varDecl->isParentInitialized()) continue; // The var was not default initializable, and did not have an explicit @@ -870,7 +870,7 @@ static void deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) { lookupVarDeclForCodingKeysCase(conformanceDC, elt, targetDecl); // Don't output a decode statement for a var let with a default value. - if (varDecl->isLet() && varDecl->getParentInitializer() != nullptr) + if (varDecl->isLet() && varDecl->isParentInitialized()) continue; auto methodName = diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 3cafa4eb026c6..18b3582508048 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -491,7 +491,7 @@ class AccessControlChecker : public AccessControlCheckerBase, void checkTypedPattern(const TypedPattern *TP, bool isTypeContext, llvm::DenseSet &seenVars) { - const VarDecl *anyVar = nullptr; + VarDecl *anyVar = nullptr; TP->forEachVariable([&](VarDecl *V) { seenVars.insert(V); anyVar = V; @@ -521,6 +521,31 @@ class AccessControlChecker : public AccessControlCheckerBase, typeAccess); highlightOffendingType(TC, diag, complainRepr); }); + + // Check the property delegate type. + if (auto attr = anyVar->getAttachedPropertyDelegate()) { + checkTypeAccess(attr->getTypeLoc(), anyVar, + /*mayBeInferred=*/false, + [&](AccessScope typeAccessScope, + const TypeRepr *complainRepr, + DowngradeToWarning downgradeToWarning) { + auto typeAccess = typeAccessScope.accessLevelForDiagnostics(); + bool isExplicit = + anyVar->getAttrs().hasAttribute() || + isa(anyVar->getDeclContext()); + auto anyVarAccess = + isExplicit ? anyVar->getFormalAccess() + : typeAccessScope.requiredAccessForDiagnostics(); + auto diag = anyVar->diagnose(diag::property_delegate_type_access, + anyVar->isLet(), + isTypeContext, + isExplicit, + anyVarAccess, + isa(anyVar->getDeclContext()), + typeAccess); + highlightOffendingType(TC, diag, complainRepr); + }); + } } void visitPatternBindingDecl(PatternBindingDecl *PBD) { @@ -1081,7 +1106,7 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, // FIXME: We need an access level to check against, so we pull one out // of some random VarDecl in the pattern. They're all going to be the // same, but still, ick. - const VarDecl *anyVar = nullptr; + VarDecl *anyVar = nullptr; TP->forEachVariable([&](VarDecl *V) { seenVars.insert(V); anyVar = V; @@ -1107,6 +1132,21 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, isTypeContext); highlightOffendingType(TC, diag, complainRepr); }); + + if (auto attr = anyVar->getAttachedPropertyDelegate()) { + checkTypeAccess(attr->getTypeLoc(), + fixedLayoutStructContext ? fixedLayoutStructContext + : anyVar, + /*mayBeInferred*/false, + [&](AccessScope typeAccessScope, + const TypeRepr *complainRepr, + DowngradeToWarning downgradeToWarning) { + auto diag = anyVar->diagnose( + diag::property_delegate_type_not_usable_from_inline, + anyVar->isLet(), isTypeContext); + highlightOffendingType(TC, diag, complainRepr); + }); + } } void visitPatternBindingDecl(PatternBindingDecl *PBD) { diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 2fd70c8e1c111..c53e46c313aa6 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -25,10 +25,12 @@ #include "swift/AST/NameLookup.h" #include "swift/AST/NameLookupRequests.h" #include "swift/AST/ParameterList.h" +#include "swift/AST/PropertyDelegates.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/AST/Types.h" #include "swift/Parse/Lexer.h" #include "swift/Sema/IDETypeChecking.h" +#include "clang/Basic/CharInfo.h" #include "llvm/Support/Debug.h" using namespace swift; @@ -127,6 +129,7 @@ class AttributeEarlyChecker : public AttributeVisitor { IGNORED_ATTR(DynamicReplacement) IGNORED_ATTR(PrivateImport) IGNORED_ATTR(Custom) + IGNORED_ATTR(PropertyDelegate) #undef IGNORED_ATTR void visitAlignmentAttr(AlignmentAttr *attr) { @@ -716,6 +719,25 @@ void TypeChecker::checkDeclAttributesEarly(Decl *D) { } } +/// Determine whether the given string is an attribute name that is +/// reserved for the implementation. +static bool isReservedAttributeName(StringRef name) { + for (unsigned i : indices(name)) { + if (name[i] == '_') + continue; + + // First character is lowercase; reserved. + if (clang::isLowercase(name[i])) + return true; + + // Everything else is reserved. + return false; + } + + // All underscores is reserved. + return true; +} + namespace { class AttributeChecker : public AttributeVisitor { TypeChecker &TC; @@ -836,6 +858,7 @@ class AttributeChecker : public AttributeVisitor { void visitNonOverrideAttr(NonOverrideAttr *attr); void visitCustomAttr(CustomAttr *attr); + void visitPropertyDelegateAttr(PropertyDelegateAttr *attr); }; } // end anonymous namespace @@ -2485,18 +2508,60 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { return; } + // If the nominal type is a property delegate type, we can be delegating + // through a property. + if (nominal->getPropertyDelegateTypeInfo()) { + // Property delegates can only be applied to variables + if (!isa(D) || isa(D)) { + TC.diagnose(attr->getLocation(), + diag::property_delegate_attribute_not_on_property, + nominal->getFullName()); + attr->setInvalid(); + return; + } + + // If this attribute isn't the one that attached a property delegate to + // this property, complain. + auto var = cast(D); + if (auto attached = var->getAttachedPropertyDelegate()) { + if (attached != attr) { + TC.diagnose(attr->getLocation(), diag::property_delegate_multiple); + TC.diagnose(attached->getLocation(), + diag::previous_property_delegate_here); + return; + } + } + + return; + } + TC.diagnose(attr->getLocation(), diag::nominal_type_not_attribute, nominal->getDescriptiveKind(), nominal->getFullName()); nominal->diagnose(diag::decl_declared_here, nominal->getFullName()); attr->setInvalid(); } + void TypeChecker::checkParameterAttributes(ParameterList *params) { for (auto param: *params) { checkDeclAttributes(param); } } +void AttributeChecker::visitPropertyDelegateAttr(PropertyDelegateAttr *attr) { + auto nominal = dyn_cast(D); + if (!nominal) + return; + + // Force checking of the property delegate type. + (void)nominal->getPropertyDelegateTypeInfo(); + + // Make sure the name isn't reserved. + if (isReservedAttributeName(nominal->getName().str())) { + nominal->diagnose(diag::property_delegate_reserved_name); + } +} + void TypeChecker::checkDeclAttributes(Decl *D) { AttributeChecker Checker(*this, D); diff --git a/lib/Sema/TypeCheckCaptures.cpp b/lib/Sema/TypeCheckCaptures.cpp index 88289207e853f..994199eab8cfb 100644 --- a/lib/Sema/TypeCheckCaptures.cpp +++ b/lib/Sema/TypeCheckCaptures.cpp @@ -62,6 +62,7 @@ class FindCapturedVars : public ASTWalker { SourceLoc &GenericParamCaptureLoc; SourceLoc &DynamicSelfCaptureLoc; DynamicSelfType *&DynamicSelf; + OpaqueValueExpr *&OpaqueValue; llvm::SmallPtrSet Diagnosed; /// The AbstractClosureExpr or AbstractFunctionDecl being analyzed. AnyFunctionRef AFR; @@ -71,11 +72,13 @@ class FindCapturedVars : public ASTWalker { SourceLoc &GenericParamCaptureLoc, SourceLoc &DynamicSelfCaptureLoc, DynamicSelfType *&DynamicSelf, + OpaqueValueExpr *&OpaqueValue, AnyFunctionRef AFR) : TC(tc), Captures(Captures), GenericParamCaptureLoc(GenericParamCaptureLoc), DynamicSelfCaptureLoc(DynamicSelfCaptureLoc), DynamicSelf(DynamicSelf), + OpaqueValue(OpaqueValue), AFR(AFR) { CaptureLoc = getCaptureLoc(AFR); } @@ -427,6 +430,11 @@ class FindCapturedVars : public ASTWalker { DynamicSelfCaptureLoc = getCaptureLoc(innerClosure); DynamicSelf = captureInfo.getDynamicSelfType(); } + + if (!OpaqueValue) { + if (captureInfo.hasOpaqueValueCapture()) + OpaqueValue = captureInfo.getOpaqueValue(); + } } bool walkToDeclPre(Decl *D) override { @@ -637,6 +645,15 @@ class FindCapturedVars : public ASTWalker { return { false, E }; } + // Capture a placeholder opaque value. + if (auto opaqueValue = dyn_cast(E)) { + if (opaqueValue->isPlaceholder()) { + assert(!OpaqueValue || OpaqueValue == opaqueValue); + OpaqueValue = opaqueValue; + return { true, E }; + } + } + return { true, E }; } }; @@ -654,14 +671,17 @@ void TypeChecker::maybeDiagnoseCaptures(Expr *E, AnyFunctionRef AFR) { if (AFR.getCaptureInfo().hasGenericParamCaptures() || AFR.getCaptureInfo().hasDynamicSelfCapture() || - AFR.getCaptureInfo().hasLocalCaptures()) { + AFR.getCaptureInfo().hasLocalCaptures() || + AFR.getCaptureInfo().hasOpaqueValueCapture()) { unsigned kind; if (AFR.getCaptureInfo().hasLocalCaptures()) kind = 0; else if (AFR.getCaptureInfo().hasGenericParamCaptures()) kind = 1; - else + else if (AFR.getCaptureInfo().hasLocalCaptures()) kind = 2; + else + kind = 3; diagnose(E->getLoc(), diag::c_function_pointer_from_function_with_context, /*closure*/ AFR.getAbstractClosureExpr() != nullptr, @@ -680,10 +700,12 @@ void TypeChecker::computeCaptures(AnyFunctionRef AFR) { SourceLoc GenericParamCaptureLoc; SourceLoc DynamicSelfCaptureLoc; DynamicSelfType *DynamicSelf = nullptr; + OpaqueValueExpr *OpaqueValuePlaceholder = nullptr; FindCapturedVars finder(*this, Captures, GenericParamCaptureLoc, DynamicSelfCaptureLoc, DynamicSelf, + OpaqueValuePlaceholder, AFR); if (AFR.getBody()) AFR.getBody()->walk(finder); @@ -706,7 +728,7 @@ void TypeChecker::computeCaptures(AnyFunctionRef AFR) { // Walk the initializers for all properties declared in the type with // an initializer. for (auto &elt : PBD->getPatternList()) { - if (elt.isInitializerLazy()) + if (elt.isInitializerSubsumed()) continue; if (auto *init = elt.getInit()) @@ -723,6 +745,10 @@ void TypeChecker::computeCaptures(AnyFunctionRef AFR) { AFR.getCaptureInfo().setGenericParamCaptures(true); } + // Anything can capture an opaque value placeholder. + if (OpaqueValuePlaceholder) + AFR.getCaptureInfo().setOpaqueValue(OpaqueValuePlaceholder); + // Only local functions capture dynamic 'Self'. if (AFR.getAsDeclContext()->getParent()->isLocalContext()) { if (GenericParamCaptureLoc.isValid()) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 0e64ee0b787d0..9b4f266d1446e 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -27,8 +27,10 @@ #include "swift/AST/ExistentialLayout.h" #include "swift/AST/Initializer.h" #include "swift/AST/NameLookup.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PrettyStackTrace.h" +#include "swift/AST/PropertyDelegates.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/SubstitutionMap.h" #include "swift/AST/TypeCheckerDebugConsumer.h" @@ -2458,21 +2460,42 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, /// Type checking listener for pattern binding initializers. class BindingListener : public ExprTypeCheckListener { + TypeChecker &tc; Pattern *&pattern; Expr *&initializer; /// The locator we're using. ConstraintLocator *Locator; + /// The context in which we are performing type checking. + DeclContext *dc; + /// The type of the initializer. Type initType; + /// Whether we applied a property delegate to the initializer expression. + PropertyDelegateTypeInfo appliedPropertyDelegate; + public: - explicit BindingListener(Pattern *&pattern, Expr *&initializer) - : pattern(pattern), initializer(initializer), - Locator(nullptr) { } + explicit BindingListener(TypeChecker &tc, Pattern *&pattern, + Expr *&initializer, DeclContext *dc) + : tc(tc), pattern(pattern), initializer(initializer), + Locator(nullptr), dc(dc) { + maybeApplyPropertyDelegate(); + } - Type getInitType() const { return initType; } + /// Retrieve the type to which the pattern should be coerced. + Type getPatternInitType() const { + if (!appliedPropertyDelegate || initType->hasError()) + return initType; + + // We applied a property delegate, so dig the pattern initialization + // type out of the value property of the delegate. + return initType->getTypeOfMember( + dc->getParentModule(), + appliedPropertyDelegate.valueVar, + appliedPropertyDelegate.valueVar->getValueInterfaceType()); + } bool builtConstraints(ConstraintSystem &cs, Expr *expr) override { assert(!expr->isSemanticallyInOutExpr()); @@ -2482,13 +2505,27 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, cs.getConstraintLocator(expr, ConstraintLocator::ContextualType); // Collect constraints from the pattern. - initType = cs.generateConstraints(pattern, Locator); - if (!initType) + Type patternType = cs.generateConstraints(pattern, Locator); + if (!patternType) return true; - // Add a conversion constraint between the types. - cs.addConstraint(ConstraintKind::Conversion, cs.getType(expr), - initType, Locator, /*isFavored*/true); + if (appliedPropertyDelegate) { + // When we have applied a property delegate, the initializer type + // is the initialization of the property delegate instance. + initType = cs.getType(expr); + + // Add a conversion constraint between the pattern type and the + // property delegate's "value" type. + cs.addConstraint(ConstraintKind::Conversion, patternType, + getPatternInitType(), Locator, /*isFavored*/true); + } else { + // The initializer type is the type of the pattern. + initType = patternType; + + // Add a conversion constraint between the types. + cs.addConstraint(ConstraintKind::Conversion, cs.getType(expr), + patternType, Locator, /*isFavored*/true); + } // The expression has been pre-checked; save it in case we fail later. initializer = expr; @@ -2513,13 +2550,97 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, assert(solution.getConstraintSystem().getType(expr)->isEqual(initType)); + // Record the property delegate type and note that the initializer has + // been subsumed by the backing property. + if (appliedPropertyDelegate) { + auto var = pattern->getSingleVar(); + var->getParentPatternBinding()->setInitializerSubsumed(0); + tc.Context.setSideCachedPropertyDelegateBackingPropertyType( + var, initType->mapTypeOutOfContext()); + + // Record the semantic initializer. + var->getAttachedPropertyDelegate()->setSemanticInit(expr); + } + initializer = expr; return expr; } + + private: + // If the pattern contains a single variable that has an attached + // property delegate, set up the initializer expression to initialize + // the backing storage. + void maybeApplyPropertyDelegate() { + auto singleVar = pattern->getSingleVar(); + if (!singleVar) + return; + + auto delegateAttr = singleVar->getAttachedPropertyDelegate(); + if (!delegateAttr) + return; + + auto delegateNominal = evaluateOrDefault( + tc.Context.evaluator, CustomAttrNominalRequest{delegateAttr, dc}, + nullptr); + if (!delegateNominal) + return; + + auto delegateInfo = delegateNominal->getPropertyDelegateTypeInfo(); + if (!delegateInfo) + return; + + Type delegateType = singleVar->getAttachedPropertyDelegateType(); + if (!delegateType) + return; + + // If the property delegate is directly initialized, form the + // call. + auto &ctx = singleVar->getASTContext(); + auto typeExpr = + TypeExpr::createImplicitHack(delegateAttr->getTypeLoc().getLoc(), + Type(delegateType), ctx); + if (delegateAttr->getArg() != nullptr) { + if (initializer) { + singleVar->diagnose(diag::property_delegate_and_normal_init, + singleVar->getFullName()) + .highlight(delegateAttr->getRange()) + .highlight(initializer->getSourceRange()); + } + + initializer = CallExpr::create( + ctx, typeExpr, delegateAttr->getArg(), + delegateAttr->getArgumentLabels(), + delegateAttr->getArgumentLabelLocs(), /*hasTrailingClosure=*/false, + /*implicit=*/false); + } else if (delegateInfo.initialValueInit) { + // FIXME: we want to use the initialValueInit we found. + assert(initializer); + initializer = CallExpr::createImplicit( + ctx, typeExpr, {initializer}, {ctx.Id_initialValue}); + } else { + singleVar->diagnose(diag::property_delegate_init_without_initial_value, + singleVar->getFullName(), delegateType) + .highlight(initializer->getSourceRange()); + ctx.Diags.diagnose(delegateAttr->getLocation(), + diag::property_delegate_direct_init) + .fixItInsertAfter(initializer->getSourceRange().End, + "(<# initializer args #>)"); + delegateNominal->diagnose(diag::kind_declname_declared_here, + delegateNominal->getDescriptiveKind(), + delegateNominal->getFullName()); + return; + } + + // Note that we have applied to property delegate, so we can adjust + // the initializer type later. + appliedPropertyDelegate = delegateInfo; + } + }; - assert(initializer && "type-checking an uninitialized binding?"); - BindingListener listener(pattern, initializer); + BindingListener listener(*this, pattern, initializer, DC); + if (!initializer) + return true; TypeLoc contextualType; auto contextualPurpose = CTP_Unused; @@ -2557,7 +2678,7 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, // FIXME: initTy should be the same as resultTy; now that typeCheckExpression() // returns a Type and not bool, we should be able to simplify the listener // implementation here. - auto initTy = listener.getInitType(); + auto initTy = listener.getPatternInitType(); if (initTy->hasDependentMember()) return true; @@ -2598,11 +2719,6 @@ bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD, Pattern *pattern = PBD->getPattern(patternNumber); Expr *init = PBD->getInit(patternNumber); - if (!init) { - PBD->setInvalid(); - return true; - } - // Enter an initializer context if necessary. PatternBindingInitializer *initContext = nullptr; DeclContext *DC = PBD->getDeclContext(); @@ -2613,6 +2729,11 @@ bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD, } bool hadError = typeCheckBinding(pattern, init, DC); + if (!init) { + PBD->setInvalid(); + return true; + } + PBD->setPattern(patternNumber, pattern, initContext); PBD->setInit(patternNumber, init); diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 1ce596d3dc596..cd9a701f74d8a 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -33,6 +33,7 @@ #include "swift/AST/Initializer.h" #include "swift/AST/NameLookup.h" #include "swift/AST/PrettyStackTrace.h" +#include "swift/AST/PropertyDelegates.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/ReferencedNameTracker.h" #include "swift/AST/TypeWalker.h" @@ -983,11 +984,12 @@ static void validatePatternBindingEntry(TypeChecker &tc, // even though the PBD is inside a TopLevelCodeDecl. TypeResolutionOptions options(TypeResolverContext::PatternBindingDecl); - if (binding->getInit(entryNumber)) { + if (binding->isInitialized(entryNumber)) { // If we have an initializer, we can also have unknown types. options |= TypeResolutionFlags::AllowUnspecifiedTypes; options |= TypeResolutionFlags::AllowUnboundGenerics; } + if (tc.typeCheckPattern(pattern, binding->getDeclContext(), options)) { setBoundVarsTypeError(pattern, tc.Context); binding->setInvalid(); @@ -2015,6 +2017,21 @@ static bool computeIsGetterMutating(TypeChecker &TC, !storage->isStatic(); } + // If we have an attached property delegate, the getter is mutating if + // the "value" property of the delegate type is mutating and we're in + // a context that has value semantics. + if (auto var = dyn_cast(storage)) { + if (auto delegateInfo = var->getAttachedPropertyDelegateTypeInfo()) { + if (delegateInfo.valueVar && + (!storage->getGetter() || storage->getGetter()->isImplicit())) { + TC.validateDecl(delegateInfo.valueVar); + return delegateInfo.valueVar->isGetterMutating() && + var->isInstanceMember() && + doesContextHaveValueSemantics(var->getDeclContext()); + } + } + } + switch (storage->getReadImpl()) { case ReadImplKind::Stored: return false; @@ -2035,6 +2052,21 @@ static bool computeIsGetterMutating(TypeChecker &TC, static bool computeIsSetterMutating(TypeChecker &TC, AbstractStorageDecl *storage) { + // If we have an attached property delegate, the setter is mutating if + // the "value" property of the delegate type is mutating and we're in + // a context that has value semantics. + if (auto var = dyn_cast(storage)) { + if (auto delegateInfo = var->getAttachedPropertyDelegateTypeInfo()) { + if (delegateInfo.valueVar && + (!storage->getSetter() || storage->getSetter()->isImplicit())) { + TC.validateDecl(delegateInfo.valueVar); + return delegateInfo.valueVar->isSetterMutating() && + var->isInstanceMember() && + doesContextHaveValueSemantics(var->getDeclContext()); + } + } + } + auto impl = storage->getImplInfo(); switch (impl.getWriteImpl()) { case WriteImplKind::Immutable: @@ -2244,12 +2276,12 @@ class DeclChecker : public DeclVisitor { // Static stored properties are allowed, with restrictions // enforced below. if (isa(VD->getDeclContext()) && - !VD->isStatic()) { + !VD->isStatic() && !VD->isInvalid()) { // Enums can only have computed properties. TC.diagnose(VD->getLoc(), diag::enum_stored_property); VD->markInvalid(); } else if (isa(VD->getDeclContext()) && - !VD->isStatic() && + !VD->isStatic() && !VD->isInvalid() && !VD->getAttrs().getAttribute()) { TC.diagnose(VD->getLoc(), diag::extension_stored_property); VD->markInvalid(); @@ -2352,7 +2384,7 @@ class DeclChecker : public DeclVisitor { // If we have a type but no initializer, check whether the type is // default-initializable. If so, do it. if (PBD->getPattern(i)->hasType() && - !PBD->getInit(i) && + !PBD->isInitialized(i) && PBD->getPattern(i)->hasStorage() && !PBD->getPattern(i)->getType()->hasError()) { @@ -2385,7 +2417,7 @@ class DeclChecker : public DeclVisitor { } } - if (PBD->getInit(i)) { + if (PBD->isInitialized(i)) { // Add the attribute that preserves the "has an initializer" value across // module generation, as required for TBDGen. PBD->getPattern(i)->forEachVariable([&](VarDecl *VD) { @@ -2409,7 +2441,7 @@ class DeclChecker : public DeclVisitor { for (unsigned i = 0, e = PBD->getNumPatternEntries(); i != e; ++i) { auto entry = PBD->getPatternList()[i]; - if (entry.getInit() || isInSILMode) continue; + if (entry.isInitialized() || isInSILMode) continue; entry.getPattern()->forEachVariable([&](VarDecl *var) { // If the variable has no storage, it never needs an initializer. @@ -2480,7 +2512,7 @@ class DeclChecker : public DeclVisitor { // If the initializers in the PBD aren't checked yet, do so now. for (unsigned i = 0, e = PBD->getNumPatternEntries(); i != e; ++i) { - if (!PBD->getInit(i)) + if (!PBD->isInitialized(i)) continue; if (!PBD->isInitializerChecked(i)) @@ -2499,7 +2531,12 @@ class DeclChecker : public DeclVisitor { // If we entered an initializer context, contextualize any // auto-closures we might have created. - if (!DC->isLocalContext()) { + // Note that we don't contextualize the initializer for a property + // with a delegate, because the initializer will have been subsumed + // by the backing storage property. + if (!DC->isLocalContext() && + !(PBD->getSingleVar() && + PBD->getSingleVar()->getAttachedPropertyDelegate())) { auto *initContext = cast_or_null( entry.getInitContext()); if (initContext) { @@ -4337,7 +4374,8 @@ static bool shouldValidateMemberDuringFinalization(NominalTypeDecl *nominal, (isa(VD) && !cast(VD)->isStatic() && (cast(VD)->hasStorage() || - VD->getAttrs().hasAttribute()))) + VD->getAttrs().hasAttribute() || + cast(VD)->getAttachedPropertyDelegate()))) return true; // For classes, we need to validate properties and functions, @@ -4433,6 +4471,12 @@ static void finalizeType(TypeChecker &TC, NominalTypeDecl *nominal) { finalizeAbstractStorageDecl(TC, prop); completeLazyVarImplementation(prop); } + + // Ensure that we create the backing variable for a property delegate. + if (prop->getAttachedPropertyDelegate()) { + finalizeAbstractStorageDecl(TC, prop); + (void)prop->getPropertyDelegateBackingProperty(); + } } if (auto *CD = dyn_cast(nominal)) { @@ -4946,6 +4990,31 @@ static void diagnoseClassWithoutInitializers(TypeChecker &tc, } } + // Lazily construct a mapping from backing storage properties to the + // declared properties. + bool computedBackingToOriginalVars = false; + llvm::SmallDenseMap backingToOriginalVars; + auto getOriginalVar = [&](VarDecl *var) -> VarDecl * { + // If we haven't computed the mapping yet, do so now. + if (!computedBackingToOriginalVars) { + for (auto member : classDecl->getMembers()) { + if (auto var = dyn_cast(member)) { + if (auto backingVar = var->getPropertyDelegateBackingProperty()) { + backingToOriginalVars[backingVar] = var; + } + } + } + + computedBackingToOriginalVars = true; + } + + auto known = backingToOriginalVars.find(var); + if (known == backingToOriginalVars.end()) + return nullptr; + + return known->second; + }; + for (auto member : classDecl->getMembers()) { auto pbd = dyn_cast(member); if (!pbd) @@ -4956,12 +5025,19 @@ static void diagnoseClassWithoutInitializers(TypeChecker &tc, continue; for (auto entry : pbd->getPatternList()) { - if (entry.getInit()) continue; + if (entry.isInitialized()) continue; SmallVector vars; entry.getPattern()->collectVariables(vars); if (vars.empty()) continue; + // Replace the variables we found with the originals for diagnostic + // purposes. + for (auto &var : vars) { + if (auto originalVar = getOriginalVar(var)) + var = originalVar; + } + auto varLoc = vars[0]->getLoc(); Optional diag; @@ -5191,6 +5267,7 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) { } } else { + SmallPtrSet backingStorageVars; for (auto member : decl->getMembers()) { if (auto ctor = dyn_cast(member)) { // Initializers that were synthesized to fulfill derived conformances @@ -5213,11 +5290,24 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) { } if (auto var = dyn_cast(member)) { + // If this variable has a property delegate, go validate it to ensure + // that we create the backing storage property. + if (auto backingVar = var->getPropertyDelegateBackingProperty()) { + validateDecl(var); + maybeAddAccessorsToStorage(var); + + backingStorageVars.insert(backingVar); + } + + // Ignore the backing storage for properties with attached delegates. + if (backingStorageVars.count(var) > 0) + continue; + if (isMemberwiseInitialized(var)) { // Initialized 'let' properties have storage, but don't get an argument // to the memberwise initializer since they already have an initial // value that cannot be overridden. - if (var->isLet() && var->getParentInitializer()) { + if (var->isLet() && var->isParentInitialized()) { // We cannot handle properties like: // let (a,b) = (1,2) @@ -5242,9 +5332,9 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) { // synthesize an initial value (e.g. for an optional) then we suppress // generation of the default initializer. if (auto pbd = dyn_cast(member)) { - if (pbd->hasStorage() && !pbd->isStatic() && !pbd->isImplicit()) + if (pbd->hasStorage() && !pbd->isStatic()) { for (auto entry : pbd->getPatternList()) { - if (entry.getInit()) continue; + if (entry.isInitialized()) continue; // If one of the bound variables is @NSManaged, go ahead no matter // what. @@ -5259,6 +5349,7 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) { if (CheckDefaultInitializer && !pbd->isDefaultInitializable()) SuppressDefaultInitializer = true; } + } continue; } } diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 184fd4f57f605..a39509187cf5c 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1325,6 +1325,7 @@ namespace { UNINTERESTING_ATTR(HasInitialValue) UNINTERESTING_ATTR(ImplementationOnly) UNINTERESTING_ATTR(Custom) + UNINTERESTING_ATTR(PropertyDelegate) #undef UNINTERESTING_ATTR void visitAvailableAttr(AvailableAttr *attr) { diff --git a/lib/Sema/TypeCheckError.cpp b/lib/Sema/TypeCheckError.cpp index 0fbbda4741f38..97e5d4f7983e9 100644 --- a/lib/Sema/TypeCheckError.cpp +++ b/lib/Sema/TypeCheckError.cpp @@ -934,6 +934,10 @@ class Context { return Context(Kind::CatchGuard); } + static Context forPatternBinding(PatternBindingDecl *binding) { + return getContextForPatternBinding(binding); + } + Context withInterpolatedString(InterpolatedStringLiteralExpr *E) const { Context copy = *this; copy.InterpolatedString = E; @@ -1659,3 +1663,9 @@ void TypeChecker::checkEnumElementErrorHandling(EnumElementDecl *elt) { init->walk(checker); } } + +void TypeChecker::checkPropertyDelegateErrorHandling( + PatternBindingDecl *binding, Expr *expr) { + CheckErrorCoverage checker(*this, Context::forPatternBinding(binding)); + expr->walk(checker); +} diff --git a/lib/Sema/TypeCheckPropertyDelegate.cpp b/lib/Sema/TypeCheckPropertyDelegate.cpp new file mode 100644 index 0000000000000..c71a4b1d54f71 --- /dev/null +++ b/lib/Sema/TypeCheckPropertyDelegate.cpp @@ -0,0 +1,398 @@ +//===--- TypeCheckPropertyDelegate.cpp - Property Delegates ---------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file implements semantic analysis for property delegates. +// +//===----------------------------------------------------------------------===// +#include "TypeChecker.h" +#include "TypeCheckType.h" +#include "swift/AST/ASTContext.h" +#include "swift/AST/Decl.h" +#include "swift/AST/DiagnosticsSema.h" +#include "swift/AST/LazyResolver.h" +#include "swift/AST/NameLookupRequests.h" +#include "swift/AST/PropertyDelegates.h" +#include "swift/AST/TypeCheckRequests.h" +using namespace swift; + +/// Find the named property in a property delegate to which access will +/// be delegated. +static VarDecl *findValueProperty(ASTContext &ctx, NominalTypeDecl *nominal, + Identifier name, bool allowMissing) { + SmallVector vars; + { + SmallVector decls; + nominal->lookupQualified(nominal, name, NL_QualifiedDefault, decls); + for (const auto &foundDecl : decls) { + auto foundVar = dyn_cast(foundDecl); + if (!foundVar || foundVar->isStatic() || + foundVar->getDeclContext() != nominal) + continue; + + vars.push_back(foundVar); + } + } + + // Diagnose missing or ambiguous properties. + switch (vars.size()) { + case 0: + if (!allowMissing) { + nominal->diagnose(diag::property_delegate_no_value_property, + nominal->getDeclaredType(), name); + } + return nullptr; + + case 1: + break; + + default: + nominal->diagnose(diag::property_delegate_ambiguous_value_property, + nominal->getDeclaredType(), name); + for (auto var : vars) { + var->diagnose(diag::kind_declname_declared_here, + var->getDescriptiveKind(), var->getFullName()); + } + return nullptr; + } + + // The property must be as accessible as the nominal type. + VarDecl *var = vars.front(); + if (var->getFormalAccess() < nominal->getFormalAccess()) { + var->diagnose(diag::property_delegate_type_requirement_not_accessible, + var->getFormalAccess(), var->getDescriptiveKind(), + var->getFullName(), nominal->getDeclaredType(), + nominal->getFormalAccess()); + return nullptr; + } + + return var; +} + +/// Determine whether we have a suitable init(initialValue:) within a property +/// delegate type. +static ConstructorDecl *findInitialValueInit(ASTContext &ctx, + NominalTypeDecl *nominal, + VarDecl *valueVar) { + SmallVector initialValueInitializers; + DeclName initName(ctx, DeclBaseName::createConstructor(), + {ctx.Id_initialValue}); + SmallVector decls; + nominal->lookupQualified(nominal, initName, NL_QualifiedDefault, decls); + for (const auto &decl : decls) { + auto init = dyn_cast(decl); + if (!init || init->getDeclContext() != nominal) + continue; + + initialValueInitializers.push_back(init); + } + + switch (initialValueInitializers.size()) { + case 0: + return nullptr; + + case 1: + break; + + default: + // Diagnose ambiguous init(initialValue:) initializers. + nominal->diagnose(diag::property_delegate_ambiguous_initial_value_init, + nominal->getDeclaredType()); + for (auto init : initialValueInitializers) { + init->diagnose(diag::kind_declname_declared_here, + init->getDescriptiveKind(), init->getFullName()); + } + return nullptr; + } + + // 'init(initialValue:)' must be as accessible as the nominal type. + auto init = initialValueInitializers.front(); + if (init->getFormalAccess() < nominal->getFormalAccess()) { + init->diagnose(diag::property_delegate_type_requirement_not_accessible, + init->getFormalAccess(), init->getDescriptiveKind(), + init->getFullName(), nominal->getDeclaredType(), + nominal->getFormalAccess()); + return nullptr; + } + + // Retrieve the type of the 'value' property. + if (!valueVar->hasInterfaceType()) + ctx.getLazyResolver()->resolveDeclSignature(valueVar); + Type valueVarType = valueVar->getValueInterfaceType(); + + // Retrieve the parameter type of the initializer. + if (!init->hasInterfaceType()) + ctx.getLazyResolver()->resolveDeclSignature(init); + Type paramType; + if (auto *curriedInitType = + init->getInterfaceType()->getAs()) { + if (auto *initType = + curriedInitType->getResult()->getAs()) { + if (initType->getParams().size() == 1) { + const auto ¶m = initType->getParams()[0]; + if (!param.isInOut() && !param.isVariadic()) { + paramType = param.getPlainType(); + if (param.isAutoClosure()) { + if (auto *fnType = paramType->getAs()) + paramType = fnType->getResult(); + } + } + } + } + } + + // The parameter type must be the same as the type of `valueVar` or an + // autoclosure thereof. + if (!paramType->isEqual(valueVarType)) { + init->diagnose(diag::property_delegate_wrong_initial_value_init, paramType, + valueVarType); + valueVar->diagnose(diag::decl_declared_here, valueVar->getFullName()); + return nullptr; + } + + // The initializer must not be failable. + if (init->getFailability() != OTK_None) { + init->diagnose(diag::property_delegate_failable_initial_value_init); + return nullptr; + } + + return init; +} + +llvm::Expected +PropertyDelegateTypeInfoRequest::evaluate( + Evaluator &eval, NominalTypeDecl *nominal) const { + // We must have the @_propertyDelegate attribute to continue. + if (!nominal->getAttrs().hasAttribute()) { + return PropertyDelegateTypeInfo(); + } + + // Look for a non-static property named "value" in the property delegate + // type. + ASTContext &ctx = nominal->getASTContext(); + auto valueVar = + findValueProperty(ctx, nominal, ctx.Id_value, /*allowMissing=*/false); + if (!valueVar) + return PropertyDelegateTypeInfo(); + + PropertyDelegateTypeInfo result; + result.valueVar = valueVar; + result.initialValueInit = findInitialValueInit(ctx, nominal, valueVar); + result.delegateValueVar = + findValueProperty(ctx, nominal, ctx.Id_delegateValue, /*allowMissing=*/true); + + return result; +} + +llvm::Expected +AttachedPropertyDelegateRequest::evaluate(Evaluator &evaluator, + VarDecl *var) const { + ASTContext &ctx = var->getASTContext(); + auto dc = var->getDeclContext(); + for (auto attr : var->getAttrs().getAttributes()) { + auto mutableAttr = const_cast(attr); + // Figure out which nominal declaration this custom attribute refers to. + auto nominal = evaluateOrDefault( + ctx.evaluator, CustomAttrNominalRequest{mutableAttr, dc}, nullptr); + + // If we didn't find a nominal type with a @_propertyDelegate attribute, + // skip this custom attribute. + if (!nominal || !nominal->getAttrs().hasAttribute()) + continue; + + // Check various restrictions on which properties can have delegates + // attached to them. + + // Local properties do not yet support delegates. + if (var->getDeclContext()->isLocalContext()) { + ctx.Diags.diagnose(attr->getLocation(), diag::property_delegate_local); + return nullptr; + } + + // Check that the variable is part of a single-variable pattern. + auto binding = var->getParentPatternBinding(); + if (!binding || binding->getSingleVar() != var) { + ctx.Diags.diagnose(attr->getLocation(), + diag::property_delegate_not_single_var); + return nullptr; + } + + // A property delegate cannot be attached to a 'let'. + if (var->isLet()) { + ctx.Diags.diagnose(attr->getLocation(), diag::property_delegate_let); + return nullptr; + } + + // Check for conflicting attributes. + if (var->getAttrs().hasAttribute() || + var->getAttrs().hasAttribute() || + var->getAttrs().hasAttribute() || + (var->getAttrs().hasAttribute() && + var->getAttrs().getAttribute()->get() != + ReferenceOwnership::Strong)) { + int whichKind; + if (var->getAttrs().hasAttribute()) + whichKind = 0; + else if (var->getAttrs().hasAttribute()) + whichKind = 1; + else if (var->getAttrs().hasAttribute()) + whichKind = 2; + else { + auto attr = var->getAttrs().getAttribute(); + whichKind = 2 + static_cast(attr->get()); + } + var->diagnose(diag::property_with_delegate_conflict_attribute, + var->getFullName(), whichKind); + return nullptr; + } + + // A property with a delegate cannot be declared in a protocol, enum, or + // an extension. + if (isa(dc) || + (isa(dc) && var->isInstanceMember()) || + (isa(dc) && var->isInstanceMember())) { + int whichKind; + if (isa(dc)) + whichKind = 0; + else if (isa(dc)) + whichKind = 1; + else + whichKind = 2; + var->diagnose(diag::property_with_delegate_in_bad_context, + var->getFullName(), whichKind) + .highlight(attr->getRange()); + + return nullptr; + } + + // Properties with delegates must not override another property. + if (auto classDecl = dyn_cast(dc)) { + if (auto overrideAttr = var->getAttrs().getAttribute()) { + var->diagnose(diag::property_with_delegate_overrides, + var->getFullName()) + .highlight(attr->getRange()); + return nullptr; + } + } + + return mutableAttr; + } + + return nullptr; +} + +llvm::Expected +AttachedPropertyDelegateTypeRequest::evaluate(Evaluator &evaluator, + VarDecl *var) const { + // Find the custom attribute for the attached property delegate. + llvm::Expected customAttrVal = + evaluator(AttachedPropertyDelegateRequest{var}); + if (!customAttrVal) + return customAttrVal.takeError(); + + // If there isn't an attached property delegate, we're done. + auto customAttr = *customAttrVal; + if (!customAttr) + return Type(); + + auto resolution = + TypeResolution::forContextual(var->getDeclContext()); + TypeResolutionOptions options(TypeResolverContext::PatternBindingDecl); + options |= TypeResolutionFlags::AllowUnboundGenerics; + + ASTContext &ctx = var->getASTContext(); + auto &tc = *static_cast(ctx.getLazyResolver()); + if (tc.validateType(customAttr->getTypeLoc(), resolution, options)) + return ErrorType::get(ctx); + + Type customAttrType = customAttr->getTypeLoc().getType(); + if (!customAttrType->getAnyNominal()) { + assert(ctx.Diags.hadAnyError()); + return ErrorType::get(ctx); + } + + return customAttrType; +} + +llvm::Expected +PropertyDelegateBackingPropertyTypeRequest::evaluate( + Evaluator &evaluator, VarDecl *var) const { + llvm::Expected rawTypeResult = + evaluator(AttachedPropertyDelegateTypeRequest{var}); + if (!rawTypeResult) + return rawTypeResult; + + Type rawType = *rawTypeResult; + if (!rawType) + return Type(); + + if (!rawType->hasUnboundGenericType()) + return rawType->mapTypeOutOfContext(); + + auto binding = var->getParentPatternBinding(); + if (!binding) + return Type(); + + // If there's an initializer of some sort, checking it will determine the + // property delegate type. + unsigned index = binding->getPatternEntryIndexForVarDecl(var); + ASTContext &ctx = var->getASTContext(); + TypeChecker &tc = *static_cast(ctx.getLazyResolver()); + if (binding->isInitialized(index)) { + tc.validateDecl(var); + if (!binding->isInitializerChecked(index)) + tc.typeCheckPatternBinding(binding, index); + + Type type = ctx.getSideCachedPropertyDelegateBackingPropertyType(var); + assert(type || ctx.Diags.hadAnyError()); + return type; + } + + // Compose the type of property delegate with the type of the property. + + // We expect an unbound generic type here that refers to a single-parameter + // generic type. + auto delegateAttr = var->getAttachedPropertyDelegate(); + auto nominal = rawType->getAnyNominal(); + auto unboundGeneric = rawType->getAs(); + if (!unboundGeneric || + unboundGeneric->getDecl() != nominal || + !nominal->getGenericParams() || + nominal->getGenericParams()->size() != 1) { + ctx.Diags.diagnose(delegateAttr->getLocation(), + diag::property_delegate_incompatible_unbound, + rawType) + .highlight(delegateAttr->getTypeLoc().getSourceRange()); + return Type(); + } + + // Compute the type of the property to plug in to the delegate type. + tc.validateDecl(var); + Type propertyType = var->getType(); + + // Form the specialized type. + Type delegateType = tc.applyUnboundGenericArguments( + unboundGeneric, nominal, delegateAttr->getLocation(), + TypeResolution::forContextual(var->getDeclContext()), { propertyType }); + + // Make sure no unbound types remain; this could happen if there are outer + // unbound types that weren't resolved by the application of the property + // type. + if (delegateType->hasUnboundGenericType()) { + ctx.Diags.diagnose(delegateAttr->getLocation(), + diag::property_delegate_incompatible_unbound, + delegateType) + .highlight(delegateAttr->getTypeLoc().getSourceRange()); + return Type(); + } + + return delegateType->mapTypeOutOfContext(); +} diff --git a/lib/Sema/TypeCheckRequestFunctions.cpp b/lib/Sema/TypeCheckRequestFunctions.cpp index a7e24839b2b79..81cbb0d63a724 100644 --- a/lib/Sema/TypeCheckRequestFunctions.cpp +++ b/lib/Sema/TypeCheckRequestFunctions.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "TypeChecker.h" #include "TypeCheckType.h" +#include "swift/AST/PropertyDelegates.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/AST/Decl.h" #include "swift/AST/ExistentialLayout.h" diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 834e0f6cdf74e..0ae0c2154c76d 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -49,6 +49,26 @@ using namespace swift; #define DEBUG_TYPE "TypeCheckStmt" +#ifndef NDEBUG +/// Determine whether the given context is for the backing property of a +/// property delegate. +static bool isPropertyDelegateBackingInitContext(DeclContext *dc) { + auto initContext = dyn_cast(dc); + if (!initContext) return false; + + auto patternInitContext = dyn_cast(initContext); + if (!patternInitContext) return false; + + auto binding = patternInitContext->getBinding(); + if (!binding) return false; + + auto singleVar = binding->getSingleVar(); + if (!singleVar) return false; + + return singleVar->getOriginalDelegatedProperty() != nullptr; +} +#endif + namespace { class ContextualizeClosures : public ASTWalker { DeclContext *ParentDC; @@ -116,8 +136,9 @@ namespace { ParentDC->getContextKind() != DeclContextKind::TopLevelCodeDecl) { // If a closure is nested within an auto closure, we'll need to update // its parent to the auto closure parent. - assert(ParentDC->getContextKind() == - DeclContextKind::AbstractClosureExpr && + assert((ParentDC->getContextKind() == + DeclContextKind::AbstractClosureExpr || + isPropertyDelegateBackingInitContext(ParentDC)) && "Incorrect parent decl context for closure"); CE->setParent(ParentDC); } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 5d52e738e7b51..938e4bf59535f 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -2040,6 +2040,8 @@ class TypeChecker final : public LazyResolver { void checkFunctionErrorHandling(AbstractFunctionDecl *D); void checkInitializerErrorHandling(Initializer *I, Expr *E); void checkEnumElementErrorHandling(EnumElementDecl *D); + void checkPropertyDelegateErrorHandling(PatternBindingDecl *binding, + Expr *expr); void addExprForDiagnosis(Expr *E1, Expr *Result) { DiagnosedExprs[E1] = Result; diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index d0147d0ee54e6..097a29ad66bc4 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -1852,6 +1852,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, qualifier); break; } + case SILInstructionKind::AssignByDelegateInst: + llvm_unreachable("not supported"); case SILInstructionKind::BindMemoryInst: { assert(RecordKind == SIL_ONE_TYPE_VALUES && "Layout should be OneTypeValues."); diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 4a596519037ed..125b5f0711c2a 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -1705,6 +1705,8 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { addValueRef(operand)); break; } + case SILInstructionKind::AssignByDelegateInst: + llvm_unreachable("not supported"); case SILInstructionKind::BindMemoryInst: { auto *BI = cast(&SI); SILValue baseOperand = BI->getBase(); diff --git a/test/IDE/complete_decl_attribute.swift b/test/IDE/complete_decl_attribute.swift index 0782cff3ea0a6..13815c5d1ae18 100644 --- a/test/IDE/complete_decl_attribute.swift +++ b/test/IDE/complete_decl_attribute.swift @@ -66,6 +66,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: _propertyDelegate[#Class Attribute#]; name=_propertyDelegate // KEYWORD3-NEXT: End completions @#^KEYWORD3_2^#IB @@ -80,6 +81,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: _propertyDelegate[#Enum Attribute#]; name=_propertyDelegate // KEYWORD4-NEXT: End completions @@ -90,6 +92,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: _propertyDelegate[#Struct Attribute#]; name=_propertyDelegate // KEYWORD5-NEXT: End completions @@ -117,4 +120,5 @@ 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: _propertyDelegate[#Declaration Attribute#]; name=_propertyDelegate // KEYWORD_LAST-NEXT: End completions diff --git a/test/IDE/print_property_delegates.swift b/test/IDE/print_property_delegates.swift new file mode 100644 index 0000000000000..22b87d6496d9b --- /dev/null +++ b/test/IDE/print_property_delegates.swift @@ -0,0 +1,38 @@ +// RUN: %empty-directory(%t) + +// Directly printing the type-checked AST +// RUN: %target-swift-ide-test -print-ast-typechecked -source-filename %s | %FileCheck %s + +@_propertyDelegate +struct Delegate { + var value: Value + + init(initialValue: Value) { + self.value = initialValue + } + + init(closure: () -> Value) { + self.value = closure() + } +} + +func foo() -> Int { return 17 } + +// CHECK: struct HasDelegates { +struct HasDelegates { + // CHECK: @Delegate var x: Int { + // CHECK-NEXT: get + // CHECK: var $x: Delegate + @Delegate(closure: foo) + var x: Int + + @Delegate + var y = true + + // Memberwise initializer. + // CHECK: init(x: Delegate = Delegate(closure: foo), y: Bool = true) +} + +func trigger() { + _ = HasDelegates(y: false) +} diff --git a/test/Index/property_delegates.swift b/test/Index/property_delegates.swift new file mode 100644 index 0000000000000..6a46193a475ce --- /dev/null +++ b/test/Index/property_delegates.swift @@ -0,0 +1,42 @@ +// RUN: %target-swift-ide-test -print-indexed-symbols -source-filename %s | %FileCheck -check-prefix=CHECK %s + +@_propertyDelegate +public struct Wrapper { + public var value: T + + public init(initialValue: T) { + self.value = initialValue + } + + public init(body: () -> T) { + self.value = body() + } +} + +var globalInt: Int { return 17 } + +public struct HasDelegates { + @Wrapper + public var x: Int = globalInt + + @Wrapper(body: { globalInt }) + public var y: Int + + @Wrapper(body: { return globalInt }) + public var z: Int +} + +func useMemberwiseInits(i: Int) { + _ = HasDelegates(x: i) + _ = HasDelegates(y: Wrapper(initialValue: i)) +} + +// CHECK: 19:4 | struct/Swift | Wrapper | s:14swift_ide_test7WrapperV | Ref | rel: 0 +// CHECK: 20:14 | instance-property/Swift | x | s:14swift_ide_test12HasDelegatesV1xSivp | Def,RelChild | rel: 1 +// CHECK: 20:23 | variable/Swift | globalInt | s:14swift_ide_test9globalIntSivp | Ref,Read | rel: 0 + +// CHECK: 22:4 | struct/Swift | Wrapper | s:14swift_ide_test7WrapperV | Ref | rel: 0 +// CHECK: 22:20 | variable/Swift | globalInt | s:14swift_ide_test9globalIntSivp | Ref,Read | rel: 0 + +// CHECK: 25:4 | struct/Swift | Wrapper | s:14swift_ide_test7WrapperV | Ref | rel: 0 +// CHECK: 25:27 | variable/Swift | globalInt | s:14swift_ide_test9globalIntSivp | Ref,Read | rel: 0 diff --git a/test/Index/roles.swift b/test/Index/roles.swift index 0ab6360880fa1..f35988d72062e 100644 --- a/test/Index/roles.swift +++ b/test/Index/roles.swift @@ -503,3 +503,15 @@ _ = \StructWithKeypath.[0] // CHECK: [[@LINE-1]]:24 | instance-property/subscript/Swift | subscript(_:) | s:14swift_ide_test17StructWithKeypathVyS2icip | Ref,Read | rel: 0 // CHECK: [[@LINE-2]]:24 | instance-method/acc-get/Swift | getter:subscript(_:) | s:14swift_ide_test17StructWithKeypathVyS2icig | Ref,Call,Impl | rel: 0 + +struct BStruct { + var x = 17 + var y = true + var z = "hello" +} + +func useDefaultInits() { + _ = BStruct(y: false) + // CHECK: [[@LINE-1]]:15 | instance-property/Swift | y | s:14swift_ide_test7BStructV1ySbvp | Ref,RelCont + // CHECK: [[@LINE-2]]:7 | constructor/Swift | init(x:y:z:) | s:14swift_ide_test7BStructV1x1y1zACSi_SbSStcfc | Ref,Call,RelCall,RelCont | rel: 1 +} diff --git a/test/Parse/dollar_identifier.swift b/test/Parse/dollar_identifier.swift index f72adb39b9b08..5406aef4c420a 100644 --- a/test/Parse/dollar_identifier.swift +++ b/test/Parse/dollar_identifier.swift @@ -58,6 +58,14 @@ func escapedDollarFunc() { func escapedDollarAnd() { // FIXME: Bad diagnostics. `$0` = 1 // expected-error {{expected expression}} - `$$` = 2 // expected-error {{expected numeric value following '$'}} - `$abc` = 3 // expected-error {{expected numeric value following '$'}} + `$$` = 2 + `$abc` = 3 +} + +func $declareWithDollar() { // expected-error{{cannot declare entity '$declareWithDollar' with a '$' prefix}} + var $foo = 17 // expected-error{{cannot declare entity '$foo' with a '$' prefix}} + func $bar() { } // expected-error{{cannot declare entity '$bar' with a '$' prefix}} + func wibble( + $a: Int, // expected-error{{cannot declare entity '$a' with a '$' prefix}} + $b c: Int) { } // expected-error{{cannot declare entity '$b' with a '$' prefix}} } diff --git a/test/PlaygroundTransform/import_error.swift b/test/PlaygroundTransform/import_error.swift index 015e8a9f35758..8e56e34d70673 100644 --- a/test/PlaygroundTransform/import_error.swift +++ b/test/PlaygroundTransform/import_error.swift @@ -2,4 +2,4 @@ // RUN: cp %s %t/main.swift // RUN: %target-swift-frontend -typecheck -playground %t/main.swift -verify -var $a = 2 // expected-error {{expected numeric value following '$'}} +var $a = 2 // expected-error {{cannot declare entity '$a' with a '$' prefix}} diff --git a/test/SILGen/property_delegates.swift b/test/SILGen/property_delegates.swift new file mode 100644 index 0000000000000..75bf4f30ed59c --- /dev/null +++ b/test/SILGen/property_delegates.swift @@ -0,0 +1,252 @@ +// RUN: %target-swift-frontend -primary-file %s -emit-silgen | %FileCheck %s +// FIXME: switch to %target-swift-emit-silgen once we have syntax tree support + +@_propertyDelegate +struct Wrapper { + var value: T +} + +@_propertyDelegate +struct WrapperWithInitialValue { + var value: T + + init(initialValue: T) { + self.value = initialValue + } +} + +protocol DefaultInit { + init() +} + +extension Int: DefaultInit { } + +struct HasMemberwiseInit { + @Wrapper(value: false) + var x: Bool + + @WrapperWithInitialValue + var y: T = T() + + @WrapperWithInitialValue(initialValue: 17) + var z: Int +} + +func forceHasMemberwiseInit() { + _ = HasMemberwiseInit(x: Wrapper(value: true), y: 17, z: WrapperWithInitialValue(initialValue: 42)) + _ = HasMemberwiseInit(x: Wrapper(value: true)) + _ = HasMemberwiseInit(y: 17) + _ = HasMemberwiseInit(z: WrapperWithInitialValue(initialValue: 42)) + _ = HasMemberwiseInit() +} + +// HasMemberwiseInit.x.setter +// CHECK-LABEL: sil hidden [transparent] [ossa] @$s18property_delegates17HasMemberwiseInitV1xSbvs : $@convention(method) (Bool, @inout HasMemberwiseInit) -> () { +// CHECK: bb0(%0 : $Bool, %1 : $*HasMemberwiseInit): +// CHECK: [[MODIFY_SELF:%.*]] = begin_access [modify] [unknown] %1 : $*HasMemberwiseInit +// CHECK: [[X_BACKING:%.*]] = struct_element_addr [[MODIFY_SELF]] : $*HasMemberwiseInit, #HasMemberwiseInit.$x +// CHECK: [[X_BACKING_VALUE:%.*]] = struct_element_addr [[X_BACKING]] : $*Wrapper, #Wrapper.value +// CHECK: assign %0 to [[X_BACKING_VALUE]] : $*Bool +// CHECK: end_access [[MODIFY_SELF]] : $*HasMemberwiseInit + +// variable initialization expression of HasMemberwiseInit.$x +// CHECK-LABEL: sil hidden [transparent] [ossa] @$s18property_delegates17HasMemberwiseInitV2$xAA7WrapperVySbGvpfi : $@convention(thin) () -> Wrapper { +// CHECK: integer_literal $Builtin.Int1, 0 +// CHECK-NOT: return +// CHECK: function_ref @$sSb22_builtinBooleanLiteralSbBi1__tcfC : $@convention(method) (Builtin.Int1, @thin Bool.Type) -> Bool +// CHECK-NOT: return +// CHECK: function_ref @$s18property_delegates7WrapperV5valueACyxGx_tcfC : $@convention(method) <τ_0_0> (@in τ_0_0, @thin Wrapper<τ_0_0>.Type) -> @out Wrapper<τ_0_0> // user: %9 +// CHECK: return {{%.*}} : $Wrapper + +// variable initialization expression of HasMemberwiseInit.$y +// CHECK-LABEL: sil hidden [transparent] [ossa] @$s18property_delegates17HasMemberwiseInitV2$yAA23WrapperWithInitialValueVyxGvpfi : $@convention(thin) () -> @out T { +// CHECK: bb0(%0 : $*T): +// CHECK-NOT: return +// CHECK: witness_method $T, #DefaultInit.init!allocator.1 : (Self.Type) -> () -> Self : $@convention(witness_method: DefaultInit) <τ_0_0 where τ_0_0 : DefaultInit> (@thick τ_0_0.Type) -> @out τ_0_0 + +// variable initialization expression of HasMemberwiseInit.$z +// CHECK-LABEL: sil hidden [transparent] [ossa] @$s18property_delegates17HasMemberwiseInitV2$zAA23WrapperWithInitialValueVySiGvpfi : $@convention(thin) () -> WrapperWithInitialValue { +// CHECK: bb0: +// CHECK-NOT: return +// CHECK: integer_literal $Builtin.IntLiteral, 17 +// CHECK-NOT: return +// CHECK: function_ref @$s18property_delegates23WrapperWithInitialValueV07initialF0ACyxGx_tcfC : $@convention(method) <τ_0_0> (@in τ_0_0, @thin WrapperWithInitialValue<τ_0_0>.Type) -> @out WrapperWithInitialValue<τ_0_0> + +// default argument 0 of HasMemberwiseInit.init(x:y:z:) +// CHECK: sil hidden [ossa] @$s18property_delegates17HasMemberwiseInitV1x1y1zACyxGAA7WrapperVySbG_xAA0F16WithInitialValueVySiGtcfcfA_ : $@convention(thin) () -> Wrapper + +// default argument 1 of HasMemberwiseInit.init(x:y:z:) +// CHECK: sil hidden [ossa] @$s18property_delegates17HasMemberwiseInitV1x1y1zACyxGAA7WrapperVySbG_xAA0F16WithInitialValueVySiGtcfcfA0_ : $@convention(thin) () -> @out T { + +// default argument 2 of HasMemberwiseInit.init(x:y:z:) +// CHECK: sil hidden [ossa] @$s18property_delegates17HasMemberwiseInitV1x1y1zACyxGAA7WrapperVySbG_xAA0F16WithInitialValueVySiGtcfcfA1_ : $@convention(thin) () -> WrapperWithInitialValue { + + +// HasMemberwiseInit.init() +// CHECK-LABEL: sil hidden [ossa] @$s18property_delegates17HasMemberwiseInitVACyxGycfC : $@convention(method) (@thin HasMemberwiseInit.Type) -> @out HasMemberwiseInit { + +// Initialization of x +// CHECK-NOT: return +// CHECK: function_ref @$s18property_delegates17HasMemberwiseInitV2$xAA7WrapperVySbGvpfi : $@convention(thin) <τ_0_0 where τ_0_0 : DefaultInit> () -> Wrapper + +// Initialization of y +// CHECK-NOT: return +// CHECK: function_ref @$s18property_delegates17HasMemberwiseInitV2$yAA23WrapperWithInitialValueVyxGvpfi : $@convention(thin) <τ_0_0 where τ_0_0 : DefaultInit> () -> @out τ_0_0 +// CHECK-NOT: return +// CHECK: function_ref @$s18property_delegates23WrapperWithInitialValueV07initialF0ACyxGx_tcfC : $@convention(method) <τ_0_0> (@in τ_0_0, @thin WrapperWithInitialValue<τ_0_0>.Type) -> @out WrapperWithInitialValue<τ_0_0> + +// Initialization of z +// CHECK-NOT: return +// CHECK: function_ref @$s18property_delegates17HasMemberwiseInitV2$zAA23WrapperWithInitialValueVySiGvpfi : $@convention(thin) <τ_0_0 where τ_0_0 : DefaultInit> () -> WrapperWithInitialValue + +// CHECK: return + +// CHECK-LABEL: sil private [ossa] @$s18property_delegates9HasNestedV1yACyxGSayxG_tc33_ +// CHECK: [[STACK_SLOT:%.*]] = alloc_stack $HasNested.PrivateDelegate> +// CHECK: [[METATYPE:%.*]] = metatype $@thin HasNested.PrivateDelegate>.Type +// CHECK: [[ARRAY_STACK_SLOT:%.*]] = alloc_stack $Array +// CHECK: store %0 to [init] [[ARRAY_STACK_SLOT]] : $*Array +// CHECK: [[INIT:%.*]] = function_ref @$s18property_delegates9HasNestedV15PrivateDelegate{{.*}}initialValue +// CHECK: [[DELEGATE_INSTANCE:%.*]] = apply [[INIT]]([[STACK_SLOT]], [[ARRAY_STACK_SLOT]], [[METATYPE]]) +// CHECK: [[DELEGATE_VALUE:%.*]] = load [take] [[STACK_SLOT]] : $*HasNested.PrivateDelegate> +// CHECK: struct $HasNested ([[DELEGATE_VALUE]] : $HasNested.PrivateDelegate>) +struct HasNested { + @_propertyDelegate + private struct PrivateDelegate { + var value: U + init(initialValue: U) { + self.value = initialValue + } + } + + @PrivateDelegate + private var y: [T] = [] + + static func blah(y: [T]) -> HasNested { + return HasNested(y: y) + } +} + +// FIXME: For now, we are only checking that we don't crash. +struct HasDefaultInit { + @Wrapper(value: true) + var x + + @WrapperWithInitialValue + var y = 25 + + static func defaultInit() -> HasDefaultInit { + return HasDefaultInit() + } + + static func memberwiseInit(x: Bool, y: Int) -> HasDefaultInit { + return HasDefaultInit(x: Wrapper(value: x), y: y) + } +} + +struct DelegateWithAccessors { + @Wrapper + var x: Int { + // CHECK-LABEL: sil hidden [ossa] @$s18property_delegates21DelegateWithAccessorsV1xSivg + // CHECK-NOT: return + // CHECK: integer_literal $Builtin.IntLiteral, 42 + return 42 + + // Synthesized setter + // CHECK-LABEL: sil hidden [transparent] [ossa] @$s18property_delegates21DelegateWithAccessorsV1xSivs : $@convention(method) (Int, @inout DelegateWithAccessors) -> () + // CHECK-NOT: return + // CHECK: struct_element_addr {{%.*}} : $*DelegateWithAccessors, #DelegateWithAccessors.$x + } + + mutating func test() { + x = 17 + } +} + +func consumeOldValue(_: Int) { } +func consumeNewValue(_: Int) { } + +struct DelegateWithDidSetWillSet { + // CHECK-LABEL: sil hidden [ossa] @$s18property_delegates022DelegateWithDidSetW + // CHECK: function_ref @$s18property_delegates022DelegateWithDidSetWillF0V1xSivw + // CHECK: struct_element_addr {{%.*}} : $*DelegateWithDidSetWillSet, #DelegateWithDidSetWillSet.$x + // CHECK-NEXT: struct_element_addr {{%.*}} : $*Wrapper, #Wrapper.value + // CHECK-NEXT: assign %0 to {{%.*}} : $*Int + // CHECK: function_ref @$s18property_delegates022DelegateWithDidSetWillF0V1xSivW + @Wrapper + var x: Int { + didSet { + consumeNewValue(oldValue) + } + + willSet { + consumeOldValue(newValue) + } + } + + mutating func test(x: Int) { + self.x = x + } +} + +@_propertyDelegate +struct WrapperWithStorageValue { + var value: T + + var delegateValue: Wrapper { + return Wrapper(value: value) + } +} + +struct UseWrapperWithStorageValue { + // UseWrapperWithStorageValue.$x.getter + // CHECK-LABEL: sil hidden [transparent] [ossa] @$s18property_delegates26UseWrapperWithStorageValueV2$xAA0D0VySiGvg : $@convention(method) (UseWrapperWithStorageValue) -> Wrapper + // CHECK-NOT: return + // CHECK: function_ref @$s18property_delegates23WrapperWithStorageValueV08delegateF0AA0C0VyxGvg + @WrapperWithStorageValue(value: 17) var x: Int +} + +@_propertyDelegate +enum Lazy { + case uninitialized(() -> Value) + case initialized(Value) + + init(initialValue: @autoclosure @escaping () -> Value) { + self = .uninitialized(initialValue) + } + + var value: Value { + mutating get { + switch self { + case .uninitialized(let initializer): + let value = initializer() + self = .initialized(value) + return value + case .initialized(let value): + return value + } + } + set { + self = .initialized(newValue) + } + } +} + +struct UseLazy { + @Lazy var foo = 17 + @Lazy var bar = T() + @Lazy var wibble = [1, 2, 3] + + // CHECK-LABEL: sil hidden [ossa] @$s18property_delegates7UseLazyV3foo3bar6wibbleACyxGSi_xSaySiGtcfC : $@convention(method) (Int, @in T, @owned Array, @thin UseLazy.Type) -> @out UseLazy + // CHECK: function_ref @$s18property_delegates7UseLazyV4$fooAA0D0OySiGvpfiSiycfu_ : $@convention(thin) (@owned Int) -> Int + // CHECK: function_ref @$s18property_delegates4LazyO12initialValueACyxGxyXA_tcfC : $@convention(method) <τ_0_0> (@owned @callee_guaranteed () -> @out τ_0_0, @thin Lazy<τ_0_0>.Type) -> @out Lazy<τ_0_0> +} + +struct X { } + +func triggerUseLazy() { + _ = UseLazy() + _ = UseLazy(foo: 17) + _ = UseLazy(bar: 17) + _ = UseLazy(wibble: [1, 2, 3]) +} diff --git a/test/SILOptimizer/di-property-delegates-errors.swift b/test/SILOptimizer/di-property-delegates-errors.swift new file mode 100644 index 0000000000000..a1cd1c7606bc9 --- /dev/null +++ b/test/SILOptimizer/di-property-delegates-errors.swift @@ -0,0 +1,48 @@ +// RUN: %target-swift-frontend -emit-sil -verify %s +@_propertyDelegate +final class ClassWrapper { + var value: T { + didSet { + print(" .. set \(value)") + } + } + + init(initialValue: T) { + print(" .. init \(initialValue)") + self.value = initialValue + } + + deinit { + print(" .. deinit \(value)") + } +} + +struct IntStructWithClassWrapper { + @ClassWrapper var wrapped: Int + + init() { + wrapped = 42 // expected-error{{'self' used before all stored properties are initialized}} + // expected-note@-1{{'self.wrapped' not initialized}} + } // expected-error{{return from initializer without initializing all stored properties}} + // expected-note@-1{{'self.wrapped' not initialized}} + + init(conditional b: Bool) { + if b { + self.$wrapped = ClassWrapper(initialValue: 32) + } else { + wrapped = 42 // expected-error{{'self' used before all stored properties are initialized}} + // expected-note@-1{{'self.wrapped' not initialized}} + } + } // expected-error{{return from initializer without initializing all stored properties}} + // expected-note@-1{{'self.wrapped' not initialized}} + + init(dynamic b: Bool) { + if b { + wrapped = 42 // expected-error{{'self' used before all stored properties are initialized}} + // expected-note@-1{{'self.wrapped' not initialized}} + } + wrapped = 27 // expected-error{{'self' used before all stored properties are initialized}} + // expected-note@-1{{'self.wrapped' not initialized}} + } // expected-error{{return from initializer without initializing all stored properties}} + // expected-note@-1{{'self.wrapped' not initialized}} +} diff --git a/test/SILOptimizer/di-property-delegates.swift b/test/SILOptimizer/di-property-delegates.swift new file mode 100644 index 0000000000000..2ed8e0b8ab046 --- /dev/null +++ b/test/SILOptimizer/di-property-delegates.swift @@ -0,0 +1,312 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -o %t/a.out +// RUN: %target-run %t/a.out | %FileCheck %s + +// REQUIRES: executable_test + +@_propertyDelegate +struct Wrapper { + var value: T { + didSet { + print(" .. set \(value)") + } + } + + init(initialValue: T) { + print(" .. init \(initialValue)") + self.value = initialValue + } +} + +protocol IntInitializable { + init(_: Int) +} + +final class Payload : CustomStringConvertible, IntInitializable { + let payload: Int + + init(_ p: Int) { + self.payload = p + print(" + payload alloc \(payload)") + } + + deinit { + print(" - payload free \(payload)") + } + + var description: String { + return "value = \(payload)" + } +} + +struct IntStruct { + @Wrapper var wrapped: Int + + init() { + wrapped = 42 + wrapped = 27 + } + + init(conditional b: Bool) { + if b { + self.$wrapped = Wrapper(initialValue: 32) + } else { + wrapped = 42 + } + } + + init(dynamic b: Bool) { + if b { + wrapped = 42 + } + wrapped = 27 + } +} + +final class IntClass { + @Wrapper var wrapped: Int + + init() { + wrapped = 42 + wrapped = 27 + } + + init(conditional b: Bool) { + if b { + self.$wrapped = Wrapper(initialValue: 32) + } else { + wrapped = 42 + } + } + + init(dynamic b: Bool) { + if b { + wrapped = 42 + } + wrapped = 27 + } +} + +struct RefStruct { + @Wrapper var wrapped: Payload + + init() { + wrapped = Payload(42) + wrapped = Payload(27) + } + + init(conditional b: Bool) { + if b { + self.$wrapped = Wrapper(initialValue: Payload(32)) + } else { + wrapped = Payload(42) + } + } + + init(dynamic b: Bool) { + if b { + wrapped = Payload(42) + } + wrapped = Payload(27) + } +} + +final class GenericClass { + @Wrapper var wrapped: T + + init() { + wrapped = T(42) + wrapped = T(27) + } + + init(conditional b: Bool) { + if b { + self.$wrapped = Wrapper(initialValue: T(32)) + } else { + wrapped = T(42) + } + } + + init(dynamic b: Bool) { + if b { + wrapped = T(42) + } + wrapped = T(27) + } +} + + +func testIntStruct() { + // CHECK: ## IntStruct + print("\n## IntStruct") + + // CHECK-NEXT: .. init 42 + // CHECK-NEXT: .. set 27 + let t1 = IntStruct() + // CHECK-NEXT: 27 + print(t1.wrapped) + + // CHECK-NEXT: .. init 42 + let t2 = IntStruct(conditional: false) + // CHECK-NEXT: 42 + print(t2.wrapped) + + // CHECK-NEXT: .. init 32 + let t3 = IntStruct(conditional: true) + // CHECK-NEXT: 32 + print(t3.wrapped) + + // CHECK-NEXT: .. init 27 + let t4 = IntStruct(dynamic: false) + // CHECK-NEXT: 27 + print(t4.wrapped) + + // CHECK-NEXT: .. init 42 + // CHECK-NEXT: .. init 27 + let t5 = IntStruct(dynamic: true) + // CHECK-NEXT: 27 + print(t5.wrapped) +} + +func testIntClass() { + // CHECK: ## IntClass + print("\n## IntClass") + + // CHECK-NEXT: .. init 42 + // CHECK-NEXT: .. set 27 + let t1 = IntClass() + // CHECK-NEXT: 27 + print(t1.wrapped) + + // CHECK-NEXT: .. init 42 + let t2 = IntClass(conditional: false) + // CHECK-NEXT: 42 + print(t2.wrapped) + + // CHECK-NEXT: .. init 32 + let t3 = IntClass(conditional: true) + // CHECK-NEXT: 32 + print(t3.wrapped) + + // CHECK-NEXT: .. init 27 + let t4 = IntClass(dynamic: false) + // CHECK-NEXT: 27 + print(t4.wrapped) + + // CHECK-NEXT: .. init 42 + // CHECK-NEXT: .. init 27 + let t5 = IntClass(dynamic: true) + // CHECK-NEXT: 27 + print(t5.wrapped) +} + +func testRefStruct() { + // CHECK: ## RefStruct + print("\n## RefStruct") + + if true { + // CHECK-NEXT: + payload alloc 42 + // CHECK-NEXT: .. init value = 42 + // CHECK-NEXT: + payload alloc 27 + // CHECK-NEXT: .. set value = 27 + // CHECK-NEXT: - payload free 42 + let t1 = RefStruct() + // CHECK-NEXT: value = 27 + print(t1.wrapped) + // CHECK-NEXT: - payload free 27 + } + if true { + // CHECK-NEXT: + payload alloc 42 + // CHECK-NEXT: .. init value = 42 + let t2 = RefStruct(conditional: false) + // CHECK-NEXT: value = 42 + print(t2.wrapped) + // CHECK-NEXT: - payload free 42 + } + if true { + // CHECK-NEXT: + payload alloc 32 + // CHECK-NEXT: .. init value = 32 + let t3 = RefStruct(conditional: true) + // CHECK-NEXT: value = 32 + print(t3.wrapped) + // CHECK-NEXT: - payload free 32 + } + if true { + // CHECK-NEXT: + payload alloc 27 + // CHECK-NEXT: .. init value = 27 + let t4 = RefStruct(dynamic: false) + // CHECK-NEXT: value = 27 + print(t4.wrapped) + // CHECK-NEXT: - payload free 27 + } + if true { + // CHECK-NEXT: + payload alloc 42 + // CHECK-NEXT: .. init value = 42 + // CHECK-NEXT: + payload alloc 27 + // CHECK-NEXT: - payload free 42 + // CHECK-NEXT: .. init value = 27 + let t5 = RefStruct(dynamic: true) + // CHECK-NEXT: value = 27 + print(t5.wrapped) + // CHECK-NEXT: - payload free 27 + } +} + +func testGenericClass() { + // CHECK: ## GenericClass + print("\n## GenericClass") + + if true { + // CHECK-NEXT: + payload alloc 42 + // CHECK-NEXT: .. init value = 42 + // CHECK-NEXT: + payload alloc 27 + // CHECK-NEXT: .. set value = 27 + // CHECK-NEXT: - payload free 42 + let t1 = GenericClass() + // CHECK-NEXT: value = 27 + print(t1.wrapped) + // CHECK-NEXT: - payload free 27 + } + if true { + // CHECK-NEXT: + payload alloc 42 + // CHECK-NEXT: .. init value = 42 + let t2 = GenericClass(conditional: false) + // CHECK-NEXT: value = 42 + print(t2.wrapped) + // CHECK-NEXT: - payload free 42 + } + if true { + // CHECK-NEXT: + payload alloc 32 + // CHECK-NEXT: .. init value = 32 + let t3 = GenericClass(conditional: true) + // CHECK-NEXT: value = 32 + print(t3.wrapped) + // CHECK-NEXT: - payload free 32 + } + if true { + // CHECK-NEXT: + payload alloc 27 + // CHECK-NEXT: .. init value = 27 + let t4 = GenericClass(dynamic: false) + // CHECK-NEXT: value = 27 + print(t4.wrapped) + // CHECK-NEXT: - payload free 27 + } + if true { + // CHECK-NEXT: + payload alloc 42 + // CHECK-NEXT: .. init value = 42 + // CHECK-NEXT: + payload alloc 27 + // CHECK-NEXT: - payload free 42 + // CHECK-NEXT: .. init value = 27 + let t5 = GenericClass(dynamic: true) + // CHECK-NEXT: value = 27 + print(t5.wrapped) + // CHECK-NEXT: - payload free 27 + } +} + + +testIntStruct() +testIntClass() +testRefStruct() +testGenericClass() + diff --git a/test/SourceKit/Sema/sema_playground.swift.response b/test/SourceKit/Sema/sema_playground.swift.response index 9659e116e45a6..eed06b64d476c 100644 --- a/test/SourceKit/Sema/sema_playground.swift.response +++ b/test/SourceKit/Sema/sema_playground.swift.response @@ -11,7 +11,7 @@ key.column: 5, key.filepath: sema_playground.swift, key.severity: source.diagnostic.severity.error, - key.description: "expected numeric value following '$'", + key.description: "cannot declare entity '$blah' with a '$' prefix", key.diagnostic_stage: source.diagnostic.stage.swift.sema } ] diff --git a/test/decl/var/property_delegates.swift b/test/decl/var/property_delegates.swift new file mode 100644 index 0000000000000..07af58c3ba451 --- /dev/null +++ b/test/decl/var/property_delegates.swift @@ -0,0 +1,674 @@ +// RUN: %target-typecheck-verify-swift -swift-version 5 + +// --------------------------------------------------------------------------- +// Property delegate type definitions +// --------------------------------------------------------------------------- +@_propertyDelegate +struct Wrapper { + var value: T +} + +@_propertyDelegate +struct WrapperWithInitialValue { + var value: T + + init(initialValue: T) { + self.value = initialValue + } +} + +@_propertyDelegate +struct WrapperAcceptingAutoclosure { + private let fn: () -> T + + var value: T { + return fn() + } + + init(initialValue fn: @autoclosure @escaping () -> T) { + self.fn = fn + } + + init(body fn: @escaping () -> T) { + self.fn = fn + } +} + +@_propertyDelegate +struct MissingValue { } +// expected-error@-1{{property delegate type 'MissingValue' does not contain a non-static property named 'value'}} + +@_propertyDelegate +struct StaticValue { + static var value: Int = 17 +} +// expected-error@-3{{property delegate type 'StaticValue' does not contain a non-static property named 'value'}} + + +// expected-error@+1{{'@_propertyDelegate' attribute cannot be applied to this declaration}} +@_propertyDelegate +protocol CannotBeADelegate { + associatedtype Value + var value: Value { get set } +} + +@_propertyDelegate +struct NonVisibleValueDelegate { + private var value: Value // expected-error{{private property 'value' cannot have more restrictive access than its enclosing property delegate type 'NonVisibleValueDelegate' (which is internal)}} +} + +@_propertyDelegate +struct NonVisibleInitDelegate { + var value: Value + + private init(initialValue: Value) { // expected-error{{private initializer 'init(initialValue:)' cannot have more restrictive access than its enclosing property delegate type 'NonVisibleInitDelegate' (which is internal)}} + self.value = initialValue + } +} + +@_propertyDelegate +struct InitialValueTypeMismatch { + var value: Value // expected-note{{'value' declared here}} + + init(initialValue: Value?) { // expected-error{{'init(initialValue:)' parameter type ('Value?') must be the same as its 'value' property type ('Value') or an @autoclosure thereof}} + self.value = initialValue! + } +} + +@_propertyDelegate +struct MultipleInitialValues { // expected-error{{property delegate type 'MultipleInitialValues' has multiple initial-value initializers}} + var value: Value? = nil + + init(initialValue: Int) { // expected-note{{initializer 'init(initialValue:)' declared here}} + } + + init(initialValue: Double) { // expected-note{{initializer 'init(initialValue:)' declared here}} + } +} + +@_propertyDelegate +struct InitialValueFailable { + var value: Value + + init?(initialValue: Value) { // expected-error{{'init(initialValue:)' cannot be failable}} + return nil + } +} + +@_propertyDelegate +struct InitialValueFailableIUO { + var value: Value + + init!(initialValue: Value) { // expected-error{{'init(initialValue:)' cannot be failable}} + return nil + } +} + +// --------------------------------------------------------------------------- +// Property delegate type definitions +// --------------------------------------------------------------------------- +@_propertyDelegate +struct _lowercaseDelegate { // expected-error{{property delegate type name must start with an uppercase letter}} + var value: T +} + +@_propertyDelegate +struct _UppercaseDelegate { + var value: T +} + +// --------------------------------------------------------------------------- +// Limitations on where property delegates can be used +// --------------------------------------------------------------------------- + +func testLocalContext() { + @WrapperWithInitialValue // expected-error{{property delegates are not yet supported on local properties}} + var x = 17 + x = 42 + _ = x +} + +enum SomeEnum { + case foo + + @Wrapper(value: 17) + var bar: Int // expected-error{{property 'bar' declared inside an enum cannot have a delegate}} + // expected-error@-1{{enums must not contain stored properties}} + + @Wrapper(value: 17) + static var x: Int +} + +protocol SomeProtocol { + @Wrapper(value: 17) + var bar: Int // expected-error{{property 'bar' declared inside a protocol cannot have a delegate}} + // expected-error@-1{{property in protocol must have explicit { get } or { get set } specifier}} + + @Wrapper(value: 17) + static var x: Int // expected-error{{property 'x' declared inside a protocol cannot have a delegate}} + // expected-error@-1{{property in protocol must have explicit { get } or { get set } specifier}} +} + +struct HasDelegate { } + +extension HasDelegate { + @Wrapper(value: 17) + var inExt: Int // expected-error{{property 'inExt' declared inside an extension cannot have a delegate}} + // expected-error@-1{{extensions must not contain stored properties}} + + @Wrapper(value: 17) + static var x: Int +} + +class ClassWithDelegates { + @Wrapper(value: 17) + var x: Int +} + +class Superclass { + var x: Int = 0 +} + +class SubclassWithDelegate: Superclass { + @Wrapper(value: 17) + override var x: Int { get { return 0 } set { } } // expected-error{{property 'x' with attached delegate cannot override another property}} +} + +class C { } + +struct BadCombinations { + @WrapperWithInitialValue + lazy var x: C = C() // expected-error{{property 'x' with a delegate cannot also be lazy}} + @Wrapper + weak var y: C? // expected-error{{property 'y' with a delegate cannot also be weak}} + @Wrapper + unowned var z: C // expected-error{{property 'z' with a delegate cannot also be unowned}} +} + +struct MultipleDelegates { + @Wrapper(value: 17) // expected-error{{only one property delegate can be attached to a given property}} + @WrapperWithInitialValue // expected-note{{previous property delegate specified here}} + var x: Int = 17 + + @WrapperWithInitialValue // expected-error 2{{property delegate can only apply to a single variable}} + var (y, z) = (1, 2) +} + +// --------------------------------------------------------------------------- +// Initialization +// --------------------------------------------------------------------------- + +struct Initialization { + @Wrapper(value: 17) + var x: Int + + @Wrapper(value: 17) + var x2: Double + + @Wrapper(value: 17) + var x3 = 42 // expected-error{{property 'x3' with attached delegate cannot initialize both the delegate type and the property}} + + @Wrapper(value: 17) + var x4 + + @WrapperWithInitialValue + var y = true + + // FIXME: It would be nice if we had a more detailed diagnostic here. + @WrapperWithInitialValue + var y2 = true // expected-error{{'Bool' is not convertible to 'Int'}} + + mutating func checkTypes(s: String) { + x2 = s // expected-error{{cannot assign value of type 'String' to type 'Double'}} + x4 = s // expected-error{{cannot assign value of type 'String' to type 'Int'}} + y = s // expected-error{{cannot assign value of type 'String' to type 'Bool'}} + } +} + +// --------------------------------------------------------------------------- +// Delegate type formation +// --------------------------------------------------------------------------- +@_propertyDelegate +struct IntWrapper { + var value: Int +} + +@_propertyDelegate +struct WrapperForHashable { + var value: T +} + +@_propertyDelegate +struct WrapperWithTwoParams { + var value: (T, U) +} + +struct NotHashable { } + +struct UseWrappersWithDifferentForm { + @IntWrapper + var x: Int + + @WrapperForHashable // expected-error{{type 'NotHashable' does not conform to protocol 'Hashable'}} + var y: NotHashable + + @WrapperForHashable + var yOkay: Int + + @WrapperWithTwoParams // expected-error{{property delegate type 'WrapperWithTwoParams' must either specify all generic arguments or require only a single generic argument}} + var z: Int + + @HasNestedDelegate.NestedDelegate // expected-error{{property delegate type 'HasNestedDelegate.NestedDelegate' must either specify all generic arguments or require only a single generic argument}} + var w: Int + + @HasNestedDelegate.NestedDelegate + var wOkay: Int +} + + +// --------------------------------------------------------------------------- +// Nested delegates +// --------------------------------------------------------------------------- +struct HasNestedDelegate { + @_propertyDelegate + struct NestedDelegate { + var value: U + init(initialValue: U) { + self.value = initialValue + } + } + + @NestedDelegate + var y: [T] = [] +} + +struct UsesNestedDelegate { + @HasNestedDelegate.NestedDelegate + var y: [V] +} + +// --------------------------------------------------------------------------- +// Referencing the backing store +// --------------------------------------------------------------------------- +struct BackingStore { + @Wrapper + var x: T + + @WrapperWithInitialValue + private var y = true // expected-note{{'y' declared here}} + + func getXStorage() -> Wrapper { + return $x + } + + func getYStorage() -> WrapperWithInitialValue { + return self.$y + } +} + +func testBackingStore(bs: BackingStore) { + _ = bs.x + _ = bs.y // expected-error{{'y' is inaccessible due to 'private' protection level}} +} + +// --------------------------------------------------------------------------- +// Explicitly-specified accessors +// --------------------------------------------------------------------------- +struct DelegateWithAccessors { + @Wrapper + var x: Int { + return $x.value * 2 + } + + @WrapperWithInitialValue + var y: Int { + get { + return $y.value + } + + set { + $y.value = newValue / 2 + } + } + + mutating func test() { + x = y + y = x + } +} + +struct UseWillSetDidSet { + @Wrapper + var x: Int { + willSet { + print(newValue) + } + } + + @Wrapper + var y: Int { + didSet { + print(oldValue) + } + } + + @Wrapper + var z: Int { + willSet { + print(newValue) + } + + didSet { + print(oldValue) + } + } +} + +// --------------------------------------------------------------------------- +// Mutating/nonmutating +// --------------------------------------------------------------------------- +@_propertyDelegate +struct DelegateWithNonMutatingSetter { + class Box { + var value: Value + init(value: Value) { + self.value = value + } + } + + var box: Box + + init(initialValue: Value) { + self.box = Box(value: initialValue) + } + + var value: Value { + get { return box.value } + nonmutating set { box.value = newValue } + } +} + +@_propertyDelegate +struct DelegateWithMutatingGetter { + var readCount = 0 + var writeCount = 0 + var stored: Value + + init(initialValue: Value) { + self.stored = initialValue + } + + var value: Value { + mutating get { + readCount += 1 + return stored + } + set { + writeCount += 1 + stored = newValue + } + } +} + +@_propertyDelegate +class ClassDelegate { + var value: Value + + init(initialValue: Value) { + self.value = initialValue + } +} + +struct UseMutatingnessDelegates { + @DelegateWithNonMutatingSetter + var x = true + + @DelegateWithMutatingGetter + var y = 17 + + @DelegateWithNonMutatingSetter // expected-error{{property delegate can only be applied to a 'var'}} + let z = 3.14159 // expected-note 2{{change 'let' to 'var' to make it mutable}} + + @ClassDelegate + var w = "Hello" +} + +func testMutatingness() { + var mutable = UseMutatingnessDelegates() + + _ = mutable.x + mutable.x = false + + _ = mutable.y + mutable.y = 42 + + _ = mutable.z + mutable.z = 2.71828 // expected-error{{cannot assign to property: 'z' is a 'let' constant}} + + _ = mutable.w + mutable.w = "Goodbye" + + let nonmutable = UseMutatingnessDelegates() // expected-note 2{{change 'let' to 'var' to make it mutable}} + + // Okay due to nonmutating setter + _ = nonmutable.x + nonmutable.x = false + + _ = nonmutable.y // expected-error{{cannot use mutating getter on immutable value: 'nonmutable' is a 'let' constant}} + nonmutable.y = 42 // expected-error{{cannot use mutating getter on immutable value: 'nonmutable' is a 'let' constant}} + + _ = nonmutable.z + nonmutable.z = 2.71828 // expected-error{{cannot assign to property: 'z' is a 'let' constant}} + + // Okay due to implicitly nonmutating setter + _ = nonmutable.w + nonmutable.w = "World" +} + +// --------------------------------------------------------------------------- +// Access control +// --------------------------------------------------------------------------- +struct HasPrivateDelegate { + @_propertyDelegate + private struct PrivateDelegate { // expected-note{{type declared here}} + var value: U + init(initialValue: U) { + self.value = initialValue + } + } + + @PrivateDelegate + var y: [T] = [] + // expected-error@-1{{property must be declared private because its property delegate type uses a private type}} + + // Okay to reference private entities from a private property + @PrivateDelegate + private var z: [T] +} + +public struct HasUsableFromInlineDelegate { + @_propertyDelegate + struct InternalDelegate { // expected-note{{type declared here}} + var value: U + init(initialValue: U) { + self.value = initialValue + } + } + + @InternalDelegate + @usableFromInline + var y: [T] = [] + // expected-error@-1{{property delegate type referenced from a '@usableFromInline' property must be '@usableFromInline' or public}} +} + +@_propertyDelegate +class Box { + private(set) var value: Value + + init(initialValue: Value) { + self.value = initialValue + } +} + +struct UseBox { + @Box + var x = 17 + + @Box + var y: Int { + get { return $y.value } + set { } + } +} + +func testBox(ub: UseBox) { + _ = ub.x + ub.x = 5 // expected-error{{cannot assign to property: 'x' is a get-only property}} + + _ = ub.y + ub.y = 20 // expected-error{{cannot assign to property: 'ub' is a 'let' constant}} + + var mutableUB = ub + _ = mutableUB.y + mutableUB.y = 20 + mutableUB = ub +} + +// --------------------------------------------------------------------------- +// Memberwise initializers +// --------------------------------------------------------------------------- +struct MemberwiseInits { + @Wrapper + var x: Bool + + @WrapperWithInitialValue + var y: T +} + +func testMemberwiseInits() { + // expected-error@+1{{type '(Wrapper, Double) -> MemberwiseInits'}} + let _: Int = MemberwiseInits.init + + _ = MemberwiseInits(x: Wrapper(value: true), y: 17) +} + +struct DefaultedMemberwiseInits { + @Wrapper(value: true) + var x: Bool + + @WrapperWithInitialValue + var y: Int = 17 + + @WrapperWithInitialValue(initialValue: 17) + var z: Int +} + +func testDefaultedMemberwiseInits() { + _ = DefaultedMemberwiseInits() + _ = DefaultedMemberwiseInits( + x: Wrapper(value: false), + y: 42, + z: WrapperWithInitialValue(initialValue: 42)) + + _ = DefaultedMemberwiseInits(y: 42) + _ = DefaultedMemberwiseInits(x: Wrapper(value: false)) + _ = DefaultedMemberwiseInits(z: WrapperWithInitialValue(initialValue: 42)) +} + +// --------------------------------------------------------------------------- +// Default initializers +// --------------------------------------------------------------------------- +struct DefaultInitializerStruct { + @Wrapper(value: true) + var x + + @WrapperWithInitialValue + var y: Int = 10 +} + +struct NoDefaultInitializerStruct { // expected-note{{'init(x:)' declared here}} + @Wrapper + var x: Bool +} + +class DefaultInitializerClass { + @Wrapper(value: true) + final var x + + @WrapperWithInitialValue + final var y: Int = 10 +} + +class NoDefaultInitializerClass { // expected-error{{class 'NoDefaultInitializerClass' has no initializers}} + @Wrapper + final var x: Bool // expected-note{{stored property 'x' without initial value prevents synthesized initializers}} +} + +func testDefaultInitializers() { + _ = DefaultInitializerStruct() + _ = DefaultInitializerClass() + _ = NoDefaultInitializerStruct() // expected-error{{missing argument for parameter 'x' in call}} +} + +// --------------------------------------------------------------------------- +// Storage references +// --------------------------------------------------------------------------- +@_propertyDelegate +struct WrapperWithStorageRef { + var value: T + + var delegateValue: Wrapper { + return Wrapper(value: value) + } +} + +extension Wrapper { + var wrapperOnlyAPI: Int { return 17 } +} + +struct TestStorageRef { + @WrapperWithStorageRef var x: Int // expected-note{{'$$x' declared here}} + + init(x: Int) { + self.$$x = WrapperWithStorageRef(value: x) + } + + mutating func test() { + let _: Wrapper = $x + let i = $x.wrapperOnlyAPI + let _: Int = i + + // x is mutable, $x is not + x = 17 + $x = Wrapper(value: 42) // expected-error{{cannot assign to property: '$x' is immutable}} + } +} + +func testStorageRef(tsr: TestStorageRef) { + let _: Wrapper = tsr.$x + _ = tsr.$$x // expected-error{{'$$x' is inaccessible due to 'private' protection level}} +} + +// --------------------------------------------------------------------------- +// Misc. semantic issues +// --------------------------------------------------------------------------- +@_propertyDelegate +struct BrokenLazy { } +// expected-error@-1{{property delegate type 'BrokenLazy' does not contain a non-static property named 'value'}} +// expected-note@-2{{'BrokenLazy' declared here}} + +struct S { + @BrokenLazy // expected-error{{struct 'BrokenLazy' cannot be used as an attribute}} + var value: Int +} + +// --------------------------------------------------------------------------- +// Closures in initializers +// --------------------------------------------------------------------------- +struct UsesExplicitClosures { + @WrapperAcceptingAutoclosure(body: { 42 }) + var x: Int + + @WrapperAcceptingAutoclosure(body: { return 42 }) + var y: Int +} diff --git a/test/decl/var/property_delegates_synthesis.swift b/test/decl/var/property_delegates_synthesis.swift new file mode 100644 index 0000000000000..22f4403902ba0 --- /dev/null +++ b/test/decl/var/property_delegates_synthesis.swift @@ -0,0 +1,55 @@ +// RUN: %target-swift-frontend -typecheck -dump-ast %s | %FileCheck %s + +@_propertyDelegate +struct Wrapper { + var value: T + + init(initialValue: T) { + self.value = initialValue + } +} + +protocol DefaultInit { + init() +} + +// CHECK: struct_decl{{.*}}"UseWrapper" +struct UseWrapper { + // CHECK: var_decl{{.*}}"wrapped" + + // CHECK: accessor_decl{{.*}}get_for=wrapped + // CHECK: member_ref_expr{{.*}}UseWrapper.$wrapped + + // CHECK: accessor_decl{{.*}}set_for=wrapped + // CHECK: member_ref_expr{{.*}}UseWrapper.$wrapped + + // CHECK: accessor_decl{{.*}}_modify_for=wrapped + // CHECK: yield_stmt + // CHECK: member_ref_expr{{.*}}UseWrapper.wrapped + @Wrapper + var wrapped = T() + + // CHECK: pattern_binding_decl implicit + // CHECK-NEXT: pattern_typed implicit type='Wrapper' + // CHECK-NEXT: pattern_named implicit type='Wrapper' '$wrapped' + // CHECK: constructor_ref_call_expr + // CHECK-NEXT: declref_expr{{.*}}Wrapper.init(initialValue:) + init() { } +} + +struct UseWillSetDidSet { + // CHECK: var_decl{{.*}}"z" + + // CHECK: accessor_decl{{.*}}set_for=z + // CHECK: member_ref_expr{{.*}}UseWillSetDidSet.$z + @Wrapper + var z: Int { + willSet { + print(newValue) + } + + didSet { + print(oldValue) + } + } +}