diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index 85b784489c61a..218046be87805 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -325,6 +325,7 @@ Entities entity-spec ::= 'fA' INDEX // default argument N+1 generator entity-spec ::= 'fi' // non-local variable initializer entity-spec ::= 'fP' // property wrapper backing initializer + entity-spec ::= 'fW' // property wrapper init from projected value entity-spec ::= 'fD' // deallocating destructor; untyped entity-spec ::= 'fd' // non-deallocating destructor; untyped entity-spec ::= 'fE' // ivar destroyer; untyped diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index a8cb1f7a1f43b..d633234003d18 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -135,6 +135,8 @@ class ASTMangler : public Mangler { std::string mangleInitializerEntity(const VarDecl *var, SymbolKind SKind); std::string mangleBackingInitializerEntity(const VarDecl *var, SymbolKind SKind = SymbolKind::Default); + std::string mangleInitFromProjectedValueEntity(const VarDecl *var, + SymbolKind SKind = SymbolKind::Default); std::string mangleNominalType(const NominalTypeDecl *decl); @@ -405,6 +407,7 @@ class ASTMangler : public Mangler { void appendInitializerEntity(const VarDecl *var); void appendBackingInitializerEntity(const VarDecl *var); + void appendInitFromProjectedValueEntity(const VarDecl *var); CanType getDeclTypeForMangling(const ValueDecl *decl, GenericSignature &genericSig, diff --git a/include/swift/AST/ASTWalker.h b/include/swift/AST/ASTWalker.h index f45f84681616c..74b5850741920 100644 --- a/include/swift/AST/ASTWalker.h +++ b/include/swift/AST/ASTWalker.h @@ -215,6 +215,10 @@ class ASTWalker { /// TapExpr. virtual bool shouldWalkIntoTapExpression() { return true; } + /// This method configures whether the the walker should visit the underlying + /// value of a property wrapper placeholder. + virtual bool shouldWalkIntoPropertyWrapperPlaceholderValue() { return true; } + /// This method configures whether the walker should visit the capture /// initializer expressions within a capture list directly, rather than /// walking the declarations. diff --git a/include/swift/AST/AnyFunctionRef.h b/include/swift/AST/AnyFunctionRef.h index aa1cef7c29ad9..67e0c3523cecd 100644 --- a/include/swift/AST/AnyFunctionRef.h +++ b/include/swift/AST/AnyFunctionRef.h @@ -18,6 +18,7 @@ #include "swift/Basic/LLVM.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" +#include "swift/AST/ParameterList.h" #include "swift/AST/Types.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerUnion.h" @@ -103,6 +104,18 @@ class AnyFunctionRef { return TheFunction.get()->getSingleExpressionBody(); } + ParameterList *getParameters() const { + if (auto *AFD = TheFunction.dyn_cast()) + return AFD->getParameters(); + return TheFunction.get()->getParameters(); + } + + bool hasPropertyWrapperParameters() const { + return llvm::any_of(*getParameters(), [](const ParamDecl *param) { + return param->hasAttachedPropertyWrapper(); + }); + } + Type getType() const { if (auto *AFD = TheFunction.dyn_cast()) return AFD->getInterfaceType(); diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 74d5f3b22f80f..bca6b40a7e9a3 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -1689,6 +1689,7 @@ class CustomAttr final : public DeclAttribute, unsigned getNumArguments() const { return numArgLabels; } bool hasArgumentLabelLocs() const { return hasArgLabelLocs; } + TypeExpr *getTypeExpr() const { return typeExpr; } TypeRepr *getTypeRepr() const; Type getType() const; diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 9a222dff5d02a..4a9a4b1971b69 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -4917,7 +4917,10 @@ class VarDecl : public AbstractStorageDecl { /// Whether this property has any attached property wrappers. bool hasAttachedPropertyWrapper() const; - + + /// Whether this var has an implicit property wrapper attribute. + bool hasImplicitPropertyWrapper() const; + /// Whether all of the attached property wrappers have an init(wrappedValue:) /// initializer. bool allAttachedPropertyWrappersHaveWrappedValueInit() const; @@ -4978,6 +4981,10 @@ class VarDecl : public AbstractStorageDecl { /// property wrapper with a \c projectedValue . VarDecl *getPropertyWrapperProjectionVar() const; + /// Retrieve the local wrapped value var for for a parameter that has + /// an attached property wrapper. + VarDecl *getPropertyWrapperWrappedValueVar() const; + /// Visit all auxiliary declarations to this VarDecl. /// /// An auxiliary declaration is a declaration synthesized by the compiler to support diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 52e0c70bbcb04..d32febf61bfb8 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4109,11 +4109,11 @@ ERROR(throw_in_nonexhaustive_catch,none, ERROR(throwing_call_in_illegal_context,none, "call can throw, but errors cannot be thrown out of " - "%select{<>|a default argument|a property initializer|a global variable initializer|an enum case raw value|a catch pattern|a catch guard expression|a defer body}0", + "%select{<>|a default argument|a property wrapper initializer|a property initializer|a global variable initializer|an enum case raw value|a catch pattern|a catch guard expression|a defer body}0", (unsigned)) ERROR(throw_in_illegal_context,none, "errors cannot be thrown out of " - "%select{<>|a default argument|a property initializer|a global variable initializer|an enum case raw value|a catch pattern|a catch guard expression|a defer body}0", + "%select{<>|a default argument|a property wrapper initializer|a property initializer|a global variable initializer|an enum case raw value|a catch pattern|a catch guard expression|a defer body}0", (unsigned)) ERROR(throwing_operator_without_try,none, @@ -4151,11 +4151,11 @@ WARNING(no_async_in_await,none, "no calls to 'async' functions occur within 'await' expression", ()) ERROR(async_call_in_illegal_context,none, "'async' call cannot occur in " - "%select{<>|a default argument|a property initializer|a global variable initializer|an enum case raw value|a catch pattern|a catch guard expression|a defer body}0", + "%select{<>|a default argument|a property wrapper initializer|a property initializer|a global variable initializer|an enum case raw value|a catch pattern|a catch guard expression|a defer body}0", (unsigned)) ERROR(await_in_illegal_context,none, "'await' operation cannot occur in " - "%select{<>|a default argument|a property initializer|a global variable initializer|an enum case raw value|a catch pattern|a catch guard expression|a defer body}0", + "%select{<>|a default argument|a property wrapper initializer|a property initializer|a global variable initializer|an enum case raw value|a catch pattern|a catch guard expression|a defer body}0", (unsigned)) ERROR(async_in_nonasync_function,none, "%select{'async'|'await'|'async let'}0 in " @@ -4197,7 +4197,7 @@ ERROR(async_let_without_await,none, "reference to async let %0 is not marked with 'await'", (DeclName)) ERROR(async_let_in_illegal_context,none, "async let %0 cannot be referenced in " - "%select{<>|a default argument|a property initializer|a global variable initializer|an enum case raw value|a catch pattern|a catch guard expression|a defer body}1", + "%select{<>|a default argument|a property wrapper initializer|a property initializer|a global variable initializer|an enum case raw value|a catch pattern|a catch guard expression|a defer body}1", (DeclName, unsigned)) ERROR(asynchandler_non_func,none, @@ -5482,8 +5482,27 @@ ERROR(property_wrapper_dynamic_self_type, none, ERROR(property_wrapper_attribute_not_on_property, none, "property wrapper attribute %0 can only be applied to a property", (Identifier)) +ERROR(property_wrapper_param_not_supported,none, + "property wrapper attribute on parameter not yet supported on %0", + (DescriptiveDeclKind)) NOTE(property_wrapper_declared_here,none, "property wrapper type %0 declared here", (Identifier)) +ERROR(property_wrapper_param_no_projection,none, + "cannot use property wrapper projection parameter; " + "wrapper %0 does not have a 'projectedValue'", (Type)) +ERROR(property_wrapper_param_no_wrapper,none, + "cannot use property wrapper projection argument; " + "parameter does not have an attached property wrapper", + ()) +ERROR(property_wrapper_param_projection_invalid,none, + "cannot use property wrapper projection argument; " + "pass wrapped value type %0 instead", (Type)) +ERROR(property_wrapper_param_mutating,none, + "property wrapper applied to parameter must have a nonmutating " + "'wrappedValue' getter", ()) +ERROR(invalid_implicit_property_wrapper,none, + "inferred projection type %0 is not a property wrapper", + (Type)) ERROR(property_wrapper_mutating_get_composed_to_get_only,none, "property wrapper %0 with a mutating getter cannot be composed inside " @@ -5505,9 +5524,9 @@ ERROR(property_with_wrapper_conflict_attribute,none, ERROR(property_wrapper_not_single_var, none, "property wrapper can only apply to a single variable", ()) ERROR(property_with_wrapper_in_bad_context,none, - "%select{|non-static |non-static }1property %0 declared inside " + "%select{|non-static|non-static}1 %2 %0 declared inside " "%select{a protocol|an extension|an enum}1 cannot have a wrapper", - (Identifier, int)) + (Identifier, int, DescriptiveDeclKind)) ERROR(property_with_wrapper_overrides,none, "property %0 with attached wrapper cannot override another property", (Identifier)) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index ba622dc2dcb58..84e172b5d4d3f 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -4228,6 +4228,62 @@ class PropertyWrapperValuePlaceholderExpr : public Expr { } }; +/// An implicit applied property wrapper expression. +class AppliedPropertyWrapperExpr final : public Expr { +public: + enum class ValueKind { + WrappedValue, + ProjectedValue + }; + +private: + /// The concrete callee which owns the property wrapper. + ConcreteDeclRef Callee; + + /// The owning declaration. + const ParamDecl *Param; + + /// The source location of the argument list. + SourceLoc Loc; + + /// The value to which the property wrapper is applied for initialization. + Expr *Value; + + /// The kind of value that the property wrapper is applied to. + ValueKind Kind; + + AppliedPropertyWrapperExpr(ConcreteDeclRef callee, const ParamDecl *param, SourceLoc loc, + Type Ty, Expr *value, ValueKind kind) + : Expr(ExprKind::AppliedPropertyWrapper, /*Implicit=*/true, Ty), + Callee(callee), Param(param), Loc(loc), Value(value), Kind(kind) {} + +public: + static AppliedPropertyWrapperExpr * + create(ASTContext &ctx, ConcreteDeclRef callee, const ParamDecl *param, SourceLoc loc, + Type Ty, Expr *value, ValueKind kind); + + SourceRange getSourceRange() const { return Loc; } + + ConcreteDeclRef getCallee() { return Callee; } + + /// Returns the parameter declaration with the attached property wrapper. + const ParamDecl *getParamDecl() const { return Param; }; + + /// Returns the value that the property wrapper is applied to. + Expr *getValue() { return Value; } + + /// Sets the value that the property wrapper is applied to. + void setValue(Expr *value) { Value = value; } + + /// Returns the kind of value, between wrapped value and projected + /// value, the property wrapper is applied to. + ValueKind getValueKind() const { return Kind; } + + static bool classof(const Expr *E) { + return E->getKind() == ExprKind::AppliedPropertyWrapper; + } +}; + /// An expression referring to a default argument left unspecified at the /// call site. /// diff --git a/include/swift/AST/ExprNodes.def b/include/swift/AST/ExprNodes.def index a1802169a9bb9..7ec7439d25585 100644 --- a/include/swift/AST/ExprNodes.def +++ b/include/swift/AST/ExprNodes.def @@ -131,6 +131,7 @@ EXPR(DynamicType, Expr) EXPR(RebindSelfInConstructor, Expr) EXPR(OpaqueValue, Expr) EXPR(PropertyWrapperValuePlaceholder, Expr) +EXPR(AppliedPropertyWrapper, Expr) EXPR(DefaultArgument, Expr) EXPR(BindOptional, Expr) EXPR(OptionalEvaluation, Expr) diff --git a/include/swift/AST/Identifier.h b/include/swift/AST/Identifier.h index 71cce7a74fd33..5d93f225c2d96 100644 --- a/include/swift/AST/Identifier.h +++ b/include/swift/AST/Identifier.h @@ -164,6 +164,10 @@ class Identifier { bool isEditorPlaceholder() const { return !empty() && isEditorPlaceholder(str()); } + + bool hasDollarPrefix() const { + return str().startswith("$") && !(getLength() == 1); + } const void *getAsOpaquePointer() const { return static_cast(Pointer); @@ -324,6 +328,10 @@ class DeclBaseName { return !isSpecial() && getIdentifier().isEditorPlaceholder(); } + bool hasDollarPrefix() const { + return getIdentifier().hasDollarPrefix(); + } + /// A representation of the name to be displayed to users. May be ambiguous /// between identifiers and special names. StringRef userFacingName() const { diff --git a/include/swift/AST/Initializer.h b/include/swift/AST/Initializer.h index bcbc408402214..18df9b744d56a 100644 --- a/include/swift/AST/Initializer.h +++ b/include/swift/AST/Initializer.h @@ -33,6 +33,9 @@ enum class InitializerKind : uint8_t { /// A function's default argument expression. DefaultArgument, + + /// A property wrapper initialization expression. + PropertyWrapper, }; /// An Initializer is a kind of DeclContext used for expressions that @@ -41,9 +44,9 @@ enum class InitializerKind : uint8_t { /// Generally, Initializers are created lazily, as most initializers /// don't really require DeclContexts. class Initializer : public DeclContext { - unsigned Kind : 1; + unsigned Kind : 2; protected: - unsigned SpareBits : 31; + unsigned SpareBits : 30; Initializer(InitializerKind kind, DeclContext *parent) : DeclContext(DeclContextKind::Initializer, parent), @@ -195,6 +198,39 @@ class SerializedDefaultArgumentInitializer : public SerializedLocalDeclContext { return false; } }; + +/// A property wrapper initialization expression. The parent context is the +/// function or closure which owns the property wrapper. +class PropertyWrapperInitializer : public Initializer { +public: + enum class Kind { + WrappedValue, + ProjectedValue + }; + +private: + ParamDecl *param; + Kind kind; + +public: + explicit PropertyWrapperInitializer(DeclContext *parent, ParamDecl *param, Kind kind) + : Initializer(InitializerKind::PropertyWrapper, parent), + param(param), kind(kind) {} + + ParamDecl *getParam() const { return param; } + + Kind getKind() const { return kind; } + + static bool classof(const DeclContext *DC) { + if (auto init = dyn_cast(DC)) + return classof(init); + return false; + } + + static bool classof(const Initializer *I) { + return I->getInitializerKind() == InitializerKind::PropertyWrapper; + } +}; } // end namespace swift diff --git a/include/swift/AST/PropertyWrappers.h b/include/swift/AST/PropertyWrappers.h index 2f78ad4123d76..f34ba02409423 100644 --- a/include/swift/AST/PropertyWrappers.h +++ b/include/swift/AST/PropertyWrappers.h @@ -29,6 +29,26 @@ class Expr; class VarDecl; class OpaqueValueExpr; +/// The kind of property initializer to look for +enum class PropertyWrapperInitKind { + /// An initial-value initializer (i.e. `init(initialValue:)`), which is + /// deprecated. + InitialValue, + /// An wrapped-value initializer (i.e. `init(wrappedValue:)`) + WrappedValue, + /// A projected-value initializer (i.e. `init(projectedValue:)`) + ProjectedValue, + /// An default-value initializer (i.e. `init()` or `init(defaultArgs...)`) + Default +}; + +/// Information about an applied property wrapper, including the backing wrapper type +/// and the initialization kind. +struct AppliedPropertyWrapper { + Type wrapperType; + PropertyWrapperInitKind initKind; +}; + /// Describes a property wrapper type. struct PropertyWrapperTypeInfo { /// The property through which access that uses this wrapper type is @@ -50,6 +70,8 @@ struct PropertyWrapperTypeInfo { HasDefaultValueInit } defaultInit = NoDefaultValueInit; + bool hasProjectedValueInit = false; + /// The property through which the projection value ($foo) will be accessed. /// /// This property is optional. If present, a computed property for `$foo` @@ -140,6 +162,14 @@ struct PropertyWrapperLValueness { void simple_display(llvm::raw_ostream &os, PropertyWrapperLValueness l); +/// Given the initializer for a property with an attached property wrapper, +/// dig out the wrapped value placeholder for the original initialization +/// expression. +/// +/// \note The wrapped value placeholder is injected for properties that can +/// be initialized out-of-line using an expression of the wrapped property type. +PropertyWrapperValuePlaceholderExpr *findWrappedValuePlaceholder(Expr *init); + /// Describes the backing property of a property that has an attached wrapper. struct PropertyWrapperBackingPropertyInfo { /// The backing property. @@ -149,30 +179,83 @@ struct PropertyWrapperBackingPropertyInfo { /// of the original wrapped property prefixed with \c $ VarDecl *projectionVar = nullptr; - /// An expression that initializes the backing property from a value of - /// the original property's type (e.g., via `init(wrappedValue:)`), or - /// \c NULL if the backing property can only be initialized directly. - Expr *initializeFromOriginal = nullptr; +private: + struct { + /// An expression that initializes the backing property from a value of + /// the original property's type via \c init(wrappedValue:) if supported + /// by the wrapper type. + Expr *expr = nullptr; + + /// When \c expr is not null, the opaque value that is used as + /// a placeholder for a value of the original property's type. + PropertyWrapperValuePlaceholderExpr *placeholder = nullptr; + } wrappedValueInit; + + struct { + /// An expression that initializes the backing property from a value of + /// the synthesized projection type via \c init(projectedValue:) if + /// supported by the wrapper type. + Expr *expr = nullptr; + + /// When \c expr is not null, the opaque value that is used as + /// a placeholder for a value of the projection type. + PropertyWrapperValuePlaceholderExpr *placeholder = nullptr; + } projectedValueInit; + +public: + PropertyWrapperBackingPropertyInfo() { } - /// 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. - PropertyWrapperValuePlaceholderExpr *wrappedValuePlaceholder = nullptr; + PropertyWrapperBackingPropertyInfo(VarDecl *backingVar, VarDecl *projectionVar) + : backingVar(backingVar), projectionVar(projectionVar) { } - PropertyWrapperBackingPropertyInfo() { } - - PropertyWrapperBackingPropertyInfo(VarDecl *backingVar, - VarDecl *projectionVar, - Expr *initializeFromOriginal, - PropertyWrapperValuePlaceholderExpr *placeholder) - : backingVar(backingVar), projectionVar(projectionVar), - initializeFromOriginal(initializeFromOriginal), - wrappedValuePlaceholder(placeholder) { } + PropertyWrapperBackingPropertyInfo(VarDecl *backingVar, VarDecl *projectionVar, + Expr *wrappedValueInitExpr, + Expr *projectedValueInitExpr) + : backingVar(backingVar), projectionVar(projectionVar) { + wrappedValueInit.expr = wrappedValueInitExpr; + if (wrappedValueInitExpr) { + wrappedValueInit.placeholder = findWrappedValuePlaceholder(wrappedValueInitExpr); + } + + projectedValueInit.expr = projectedValueInitExpr; + if (projectedValueInitExpr) { + projectedValueInit.placeholder = findWrappedValuePlaceholder(projectedValueInitExpr); + } + } /// Whether this is a valid property wrapper. bool isValid() const { return backingVar != nullptr; } + bool hasInitFromWrappedValue() const { + return wrappedValueInit.expr != nullptr; + } + + Expr *getInitFromWrappedValue() { + return wrappedValueInit.expr; + } + + PropertyWrapperValuePlaceholderExpr *getWrappedValuePlaceholder() { + return wrappedValueInit.placeholder; + } + + bool hasInitFromProjectedValue() const { + return projectedValueInit.expr != nullptr; + } + + Expr *getInitFromProjectedValue() { + return projectedValueInit.expr; + } + + PropertyWrapperValuePlaceholderExpr *getProjectedValuePlaceholder() { + return projectedValueInit.placeholder; + } + + bool hasSynthesizedInitializers() const { + return hasInitFromWrappedValue() || hasInitFromProjectedValue(); + } + explicit operator bool() const { return isValid(); } friend bool operator==(const PropertyWrapperBackingPropertyInfo &lhs, @@ -190,14 +273,6 @@ void simple_display( llvm::raw_ostream &out, const PropertyWrapperBackingPropertyInfo &backingInfo); -/// Given the initializer for a property with an attached property wrapper, -/// dig out the wrapped value placeholder for the original initialization -/// expression. -/// -/// \note The wrapped value placeholder is injected for properties that can -/// be initialized out-of-line using an expression of the wrapped property type. -PropertyWrapperValuePlaceholderExpr *findWrappedValuePlaceholder(Expr *init); - } // end namespace swift #endif // SWIFT_AST_PROPERTY_WRAPPERS_H diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 36ac9ac60e7ea..251bc4557e603 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -733,6 +733,26 @@ class PropertyWrapperBackingPropertyInfoRequest : bool isCached() const; }; +/// Request the synthesized local wrapped value var for a parameter +/// that has an attached property wrapper. +class PropertyWrapperWrappedValueVarRequest : + public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + VarDecl *evaluate(Evaluator &evaluator, VarDecl *var) const; + +public: + // Caching + bool isCached() const; +}; + /// Retrieve the structural type of an alias type. class StructuralTypeRequest : public SimpleRequest propertyWrappers; public: ParameterListInfo() { } @@ -3294,6 +3296,10 @@ struct ParameterListInfo { /// according to the "forward-scan" rule. bool acceptsUnlabeledTrailingClosureArgument(unsigned paramIdx) const; + /// The ParamDecl at the given index if the parameter has an applied + /// property wrapper. + const ParamDecl *getPropertyWrapperParam(unsigned paramIdx) const; + /// Retrieve the number of non-defaulted parameters. unsigned numNonDefaultedParameters() const { return defaultArguments.count(); diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index 6b2e498993070..4a5e96213d4a5 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -175,6 +175,7 @@ NODE(PrefixOperator) NODE(PrivateDeclName) NODE(PropertyDescriptor) CONTEXT_NODE(PropertyWrapperBackingInitializer) +CONTEXT_NODE(PropertyWrapperInitFromProjectedValue) CONTEXT_NODE(Protocol) CONTEXT_NODE(ProtocolSymbolicReference) NODE(ProtocolConformance) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 5c9279b41f8a0..c0dace360f278 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -563,7 +563,8 @@ class Parser { return consumeToken(); } - SourceLoc consumeArgumentLabel(Identifier &Result) { + SourceLoc consumeArgumentLabel(Identifier &Result, + bool diagnoseDollarPrefix) { assert(Tok.canBeArgumentLabel()); assert(Result.empty()); if (!Tok.is(tok::kw__)) { @@ -571,7 +572,7 @@ class Parser { Result = Context.getIdentifier(Tok.getText()); if (Tok.getText()[0] == '$') - diagnoseDollarIdentifier(Tok, /*diagnoseDollarPrefix=*/true); + diagnoseDollarIdentifier(Tok, diagnoseDollarPrefix); } return consumeToken(); } diff --git a/include/swift/SIL/SILDeclRef.h b/include/swift/SIL/SILDeclRef.h index 1a0ca754c7a95..1a480602b03f7 100644 --- a/include/swift/SIL/SILDeclRef.h +++ b/include/swift/SIL/SILDeclRef.h @@ -140,6 +140,10 @@ struct SILDeclRef { /// References the wrapped value injection function used to initialize /// the backing storage property from a wrapped value. PropertyWrapperBackingInitializer, + + /// References the function used to initialize a property wrapper storage + /// instance from a projected value. + PropertyWrapperInitFromProjectedValue, }; /// The ValueDecl or AbstractClosureExpr represented by this SILDeclRef. @@ -264,7 +268,8 @@ struct SILDeclRef { /// True if the SILDeclRef references the initializer for the backing storage /// of a property wrapper. bool isPropertyWrapperBackingInitializer() const { - return kind == Kind::PropertyWrapperBackingInitializer; + return (kind == Kind::PropertyWrapperBackingInitializer || + kind == Kind::PropertyWrapperInitFromProjectedValue); } /// True if the SILDeclRef references the ivar initializer or deinitializer of diff --git a/include/swift/Sema/CSFix.h b/include/swift/Sema/CSFix.h index be5a9476080ba..8672f1992bf77 100644 --- a/include/swift/Sema/CSFix.h +++ b/include/swift/Sema/CSFix.h @@ -119,6 +119,13 @@ enum class FixKind : uint8_t { /// the storage or property wrapper. UseWrappedValue, + /// Add 'var projectedValue' to the property wrapper type to allow passing + /// a projection argument. + AddProjectedValue, + + /// Add '@propertyWrapper' to a nominal type declaration. + AddPropertyWrapperAttribute, + /// Instead of spelling out `subscript` directly, use subscript operator. UseSubscriptOperator, @@ -957,6 +964,42 @@ class UseWrappedValue final : public ConstraintFix { ConstraintLocator *locator); }; +class AddProjectedValue final : public ConstraintFix { + Type wrapperType; + + AddProjectedValue(ConstraintSystem &cs, Type wrapper, + ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::AddProjectedValue, locator), wrapperType(wrapper) {} + +public: + static AddProjectedValue *create(ConstraintSystem &cs, Type wrapper, + ConstraintLocator *locator); + + std::string getName() const override { + return "add 'var projectedValue' to pass a projection argument"; + } + + bool diagnose(const Solution &solution, bool asNote = false) const override; +}; + +class AddPropertyWrapperAttribute final : public ConstraintFix { + Type wrapperType; + + AddPropertyWrapperAttribute(ConstraintSystem &cs, Type wrapper, + ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::AddPropertyWrapperAttribute, locator), wrapperType(wrapper) {} + +public: + static AddPropertyWrapperAttribute *create(ConstraintSystem &cs, Type wrapper, + ConstraintLocator *locator); + + std::string getName() const override { + return "add '@propertyWrapper'"; + } + + bool diagnose(const Solution &solution, bool asNote = false) const override; +}; + class UseSubscriptOperator final : public ConstraintFix { UseSubscriptOperator(ConstraintSystem &cs, ConstraintLocator *locator) : ConstraintFix(cs, FixKind::UseSubscriptOperator, locator) {} diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 125eba8bab6b7..baf02bfd6e252 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -1186,6 +1186,9 @@ class Solution { llvm::MapVector resultBuilderTransformed; + /// A map from argument expressions to their applied property wrapper expressions. + llvm::MapVector> appliedPropertyWrappers; + /// Simplify the given type by substituting all occurrences of /// type variables for their fixed types. Type simplifyType(Type type) const; @@ -2234,6 +2237,9 @@ class ConstraintSystem { llvm::SmallDenseMap closureEffectsCache; public: + /// A map from argument expressions to their applied property wrapper expressions. + llvm::SmallMapVector, 4> appliedPropertyWrappers; + /// The locators of \c Defaultable constraints whose defaults were used. std::vector DefaultedConstraints; @@ -2698,6 +2704,9 @@ class ConstraintSystem { unsigned numResultBuilderTransformed; + /// The length of \c appliedPropertyWrappers + unsigned numAppliedPropertyWrappers; + /// The length of \c ResolvedOverloads. unsigned numResolvedOverloads; @@ -4633,6 +4642,12 @@ class ConstraintSystem { ConstraintKind bodyResultConstraintKind, ConstraintLocatorBuilder locator); + /// Matches a wrapped or projected value parameter type to its backing + /// property wrapper type by applying the property wrapper. + TypeMatchResult applyPropertyWrapperToParameter( + Type wrapperType, Type paramType, ParamDecl *param, Identifier argLabel, + ConstraintKind matchKind, ConstraintLocatorBuilder locator); + Optional determineBestBindings(); /// Get bindings for the given type variable based on current diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 87ff56dbb4583..2ca611409d434 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -1385,6 +1385,9 @@ void swift::printContext(raw_ostream &os, DeclContext *dc) { case InitializerKind::DefaultArgument: os << "default argument initializer"; break; + case InitializerKind::PropertyWrapper: + os << "property wrapper initializer"; + break; } break; @@ -2585,6 +2588,12 @@ class PrintExpr : public ExprVisitor { PrintWithColorRAII(OS, ParenthesisColor) << ')'; } + void visitAppliedPropertyWrapperExpr(AppliedPropertyWrapperExpr *E) { + printCommon(E, "applied_property_wrapper_expr"); + printRec(E->getValue()); + PrintWithColorRAII(OS, ParenthesisColor) << ')'; + } + void visitDefaultArgumentExpr(DefaultArgumentExpr *E) { printCommon(E, "default_argument_expr"); OS << " default_args_owner="; diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 4c63128870616..5dc8af14975c5 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -166,6 +166,14 @@ std::string ASTMangler::mangleBackingInitializerEntity(const VarDecl *var, return finalize(); } +std::string ASTMangler::mangleInitFromProjectedValueEntity(const VarDecl *var, + SymbolKind SKind) { + beginMangling(); + appendInitFromProjectedValueEntity(var); + appendSymbolKind(SKind); + return finalize(); +} + std::string ASTMangler::mangleNominalType(const NominalTypeDecl *decl) { beginMangling(); appendAnyGenericType(decl); @@ -2108,6 +2116,19 @@ void ASTMangler::appendContext(const DeclContext *ctx, StringRef useModuleName) } return; } + + case InitializerKind::PropertyWrapper: { + auto wrapperInit = cast(ctx); + switch (wrapperInit->getKind()) { + case PropertyWrapperInitializer::Kind::WrappedValue: + appendBackingInitializerEntity(wrapperInit->getParam()); + break; + case PropertyWrapperInitializer::Kind::ProjectedValue: + appendInitFromProjectedValueEntity(wrapperInit->getParam()); + break; + } + return; + } } llvm_unreachable("bad initializer kind"); @@ -2806,6 +2827,11 @@ void ASTMangler::appendBackingInitializerEntity(const VarDecl *var) { appendOperator("fP"); } +void ASTMangler::appendInitFromProjectedValueEntity(const VarDecl *var) { + appendEntity(var, "vp", var->isStatic()); + appendOperator("fW"); +} + /// Is this declaration a method for mangling purposes? If so, we'll leave the /// Self type out of its mangling. static bool isMethodDecl(const Decl *decl) { diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index 52ddd8d693add..21dc9a77e674a 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -495,18 +495,32 @@ class Traversal : public ASTVisitorgetOpaqueValuePlaceholder())) - E->setOpaqueValuePlaceholder(dyn_cast(placeholder)); - else - return nullptr; - - if (E->getOriginalWrappedValue()) { - if (auto *newValue = doIt(E->getOriginalWrappedValue())) - E->setOriginalWrappedValue(newValue); + if (E->getOpaqueValuePlaceholder()) { + if (auto *placeholder = doIt(E->getOpaqueValuePlaceholder())) + E->setOpaqueValuePlaceholder(dyn_cast(placeholder)); else return nullptr; } + if (Walker.shouldWalkIntoPropertyWrapperPlaceholderValue()) { + if (E->getOriginalWrappedValue()) { + if (auto *newValue = doIt(E->getOriginalWrappedValue())) + E->setOriginalWrappedValue(newValue); + else + return nullptr; + } + } + + return E; + } + + Expr *visitAppliedPropertyWrapperExpr(AppliedPropertyWrapperExpr *E) { + if (auto *newValue = doIt(E->getValue())) { + E->setValue(newValue); + } else { + return nullptr; + } + return E; } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 198d93574438c..22a19e3ebcfa8 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -5861,7 +5861,17 @@ llvm::TinyPtrVector VarDecl::getAttachedPropertyWrappers() const { /// Whether this property has any attached property wrappers. bool VarDecl::hasAttachedPropertyWrapper() const { - return !getAttachedPropertyWrappers().empty(); + return !getAttachedPropertyWrappers().empty() || hasImplicitPropertyWrapper(); +} + +bool VarDecl::hasImplicitPropertyWrapper() const { + if (!getAttachedPropertyWrappers().empty()) + return false; + + auto *dc = getDeclContext(); + bool isClosureParam = isa(this) && + dc->getContextKind() == DeclContextKind::AbstractClosureExpr; + return !isImplicit() && getName().hasDollarPrefix() && isClosureParam; } /// Whether all of the attached property wrappers have an init(wrappedValue:) @@ -5877,15 +5887,22 @@ bool VarDecl::allAttachedPropertyWrappersHaveWrappedValueInit() const { PropertyWrapperTypeInfo VarDecl::getAttachedPropertyWrapperTypeInfo(unsigned i) const { - auto attrs = getAttachedPropertyWrappers(); - if (i >= attrs.size()) - return PropertyWrapperTypeInfo(); - - auto attr = attrs[i]; - auto dc = getDeclContext(); - ASTContext &ctx = getASTContext(); - auto nominal = evaluateOrDefault( - ctx.evaluator, CustomAttrNominalRequest{attr, dc}, nullptr); + NominalTypeDecl *nominal; + if (hasImplicitPropertyWrapper()) { + assert(i == 0); + nominal = getInterfaceType()->getAnyNominal(); + } else { + auto attrs = getAttachedPropertyWrappers(); + if (i >= attrs.size()) + return PropertyWrapperTypeInfo(); + + auto attr = attrs[i]; + auto dc = getDeclContext(); + ASTContext &ctx = getASTContext(); + nominal = evaluateOrDefault( + ctx.evaluator, CustomAttrNominalRequest{attr, dc}, nullptr); + } + if (!nominal) return PropertyWrapperTypeInfo(); @@ -5948,8 +5965,17 @@ VarDecl *VarDecl::getPropertyWrapperProjectionVar() const { return getPropertyWrapperBackingPropertyInfo().projectionVar; } +VarDecl *VarDecl::getPropertyWrapperWrappedValueVar() const { + auto &ctx = getASTContext(); + auto mutableThis = const_cast(this); + return evaluateOrDefault( + ctx.evaluator, + PropertyWrapperWrappedValueVarRequest{mutableThis}, + nullptr); +} + void VarDecl::visitAuxiliaryDecls(llvm::function_ref visit) const { - if (getDeclContext()->isTypeContext()) + if (getDeclContext()->isTypeContext() || isImplicit()) return; if (getAttrs().hasAttribute()) { @@ -5957,12 +5983,15 @@ void VarDecl::visitAuxiliaryDecls(llvm::function_ref visit) con visit(backingVar); } - if (getAttrs().hasAttribute()) { + if (getAttrs().hasAttribute() || hasImplicitPropertyWrapper()) { if (auto *backingVar = getPropertyWrapperBackingProperty()) visit(backingVar); if (auto *projectionVar = getPropertyWrapperProjectionVar()) visit(projectionVar); + + if (auto *wrappedValueVar = getPropertyWrapperWrappedValueVar()) + visit(wrappedValueVar); } } @@ -6005,10 +6034,10 @@ bool VarDecl::isPropertyMemberwiseInitializedWithWrappedType() const { Type VarDecl::getPropertyWrapperInitValueInterfaceType() const { auto wrapperInfo = getPropertyWrapperBackingPropertyInfo(); - if (!wrapperInfo || !wrapperInfo.wrappedValuePlaceholder) + if (!wrapperInfo || !wrapperInfo.getWrappedValuePlaceholder()) return Type(); - Type valueInterfaceTy = wrapperInfo.wrappedValuePlaceholder->getType(); + Type valueInterfaceTy = wrapperInfo.getWrappedValuePlaceholder()->getType(); if (valueInterfaceTy->hasArchetype()) valueInterfaceTy = valueInterfaceTy->mapTypeOutOfContext(); @@ -6239,8 +6268,13 @@ Type ParamDecl::getVarargBaseTy(Type VarArgT) { } AnyFunctionType::Param ParamDecl::toFunctionParam(Type type) const { - if (!type) - type = getInterfaceType(); + if (!type) { + if (hasAttachedPropertyWrapper() && !hasImplicitPropertyWrapper()) { + type = getPropertyWrapperBackingPropertyType(); + } else { + type = getInterfaceType(); + } + } if (isVariadic()) type = ParamDecl::getVarargBaseTy(type); diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp index 8adb9e9225d57..91923a25cea9e 100644 --- a/lib/AST/DeclContext.cpp +++ b/lib/AST/DeclContext.cpp @@ -677,6 +677,19 @@ unsigned DeclContext::printContext(raw_ostream &OS, const unsigned indent, OS << " DefaultArgument index=" << init->getIndex(); break; } + case InitializerKind::PropertyWrapper: { + auto init = cast(this); + OS << "PropertyWrapper 0x" << (void*)init->getParam() << ", kind="; + switch (init->getKind()) { + case PropertyWrapperInitializer::Kind::WrappedValue: + OS << "wrappedValue"; + break; + case PropertyWrapperInitializer::Kind::ProjectedValue: + OS << "projectedValue"; + break; + } + break; + } } break; diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index ab3d54bf7ae4b..c1abf42881ed6 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -320,6 +320,7 @@ ConcreteDeclRef Expr::getReferencedDecl(bool stopAtParenExpr) const { NO_REFERENCE(OpaqueValue); NO_REFERENCE(PropertyWrapperValuePlaceholder); + NO_REFERENCE(AppliedPropertyWrapper); NO_REFERENCE(DefaultArgument); PASS_THROUGH_REFERENCE(BindOptional, getSubExpr); @@ -633,6 +634,7 @@ bool Expr::canAppendPostfixExpression(bool appendingPostfixOperator) const { case ExprKind::RebindSelfInConstructor: case ExprKind::OpaqueValue: case ExprKind::PropertyWrapperValuePlaceholder: + case ExprKind::AppliedPropertyWrapper: case ExprKind::DefaultArgument: case ExprKind::BindOptional: case ExprKind::OptionalEvaluation: @@ -1499,13 +1501,22 @@ PropertyWrapperValuePlaceholderExpr * PropertyWrapperValuePlaceholderExpr::create(ASTContext &ctx, SourceRange range, Type ty, Expr *wrappedValue, bool isAutoClosure) { - auto *placeholder = - new (ctx) OpaqueValueExpr(range, ty, /*isPlaceholder=*/true); + OpaqueValueExpr *placeholder = nullptr; + if (ty) + placeholder = new (ctx) OpaqueValueExpr(range, ty, /*isPlaceholder=*/true); return new (ctx) PropertyWrapperValuePlaceholderExpr( range, ty, placeholder, wrappedValue, isAutoClosure); } +AppliedPropertyWrapperExpr * +AppliedPropertyWrapperExpr::create(ASTContext &ctx, ConcreteDeclRef callee, + const ParamDecl *param, + SourceLoc loc, Type Ty, Expr *value, + AppliedPropertyWrapperExpr::ValueKind kind) { + return new (ctx) AppliedPropertyWrapperExpr(callee, param, loc, Ty, value, kind); +} + const ParamDecl *DefaultArgumentExpr::getParamDecl() const { return getParameterAt(DefaultArgsOwner.getDecl(), ParamIndex); } diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index f581e5df70507..c4652ef0d3343 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -873,6 +873,7 @@ ParameterListInfo::ParameterListInfo( const ValueDecl *paramOwner, bool skipCurriedSelf) { defaultArguments.resize(params.size()); + propertyWrappers.resize(params.size()); // No parameter owner means no parameter list means no default arguments // - hand back the zeroed bitvector. @@ -920,6 +921,10 @@ ParameterListInfo::ParameterListInfo( if (allowsUnlabeledTrailingClosureParameter(param)) { acceptsUnlabeledTrailingClosures.set(i); } + + if (param->hasAttachedPropertyWrapper()) { + propertyWrappers[i] = param; + } } } @@ -934,6 +939,11 @@ bool ParameterListInfo::acceptsUnlabeledTrailingClosureArgument( acceptsUnlabeledTrailingClosures[paramIdx]; } +const ParamDecl * +ParameterListInfo::getPropertyWrapperParam(unsigned paramIdx) const { + return paramIdx < propertyWrappers.size() ? propertyWrappers[paramIdx] : nullptr; +} + /// Turn a param list into a symbolic and printable representation that does not /// include the types, something like (_:, b:, c:) std::string swift::getParamListAsString(ArrayRef params) { diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 08d9129adf20b..dc2ee46291020 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -490,17 +490,22 @@ bool PropertyWrapperBackingPropertyTypeRequest::isCached() const { bool PropertyWrapperBackingPropertyInfoRequest::isCached() const { auto var = std::get<0>(getStorage()); - return !var->getAttrs().isEmpty(); + return !var->getAttrs().isEmpty() || var->hasImplicitPropertyWrapper(); +} + +bool PropertyWrapperWrappedValueVarRequest::isCached() const { + auto var = std::get<0>(getStorage()); + return !var->getAttrs().isEmpty() || var->hasImplicitPropertyWrapper(); } bool PropertyWrapperMutabilityRequest::isCached() const { auto var = std::get<0>(getStorage()); - return !var->getAttrs().isEmpty(); + return !var->getAttrs().isEmpty() || var->hasImplicitPropertyWrapper(); } bool PropertyWrapperLValuenessRequest::isCached() const { auto var = std::get<0>(getStorage()); - return !var->getAttrs().isEmpty(); + return !var->getAttrs().isEmpty() || var->hasImplicitPropertyWrapper(); } void swift::simple_display( diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index bafb74a848e84..ffb27dad161e0 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -789,16 +789,18 @@ class ASTScopeDeclConsumerForLocalLookup bool consume(ArrayRef values, NullablePtr baseDC) override { for (auto *value: values) { + bool foundMatch = false; if (auto *varDecl = dyn_cast(value)) { // Check if the name matches any auxiliary decls not in the AST varDecl->visitAuxiliaryDecls([&](VarDecl *auxiliaryVar) { if (name.isSimpleName(auxiliaryVar->getName())) { results.push_back(auxiliaryVar); + foundMatch = true; } }); } - if (value->getName().matchesRef(name)) + if (!foundMatch && value->getName().matchesRef(name)) results.push_back(value); } diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index bcab38b9d6f5f..006a8bf711c67 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -1614,6 +1614,7 @@ bool Demangle::nodeConsumesGenericArgs(Node *node) { case Node::Kind::DefaultArgumentInitializer: case Node::Kind::Initializer: case Node::Kind::PropertyWrapperBackingInitializer: + case Node::Kind::PropertyWrapperInitFromProjectedValue: return false; default: return true; @@ -3275,6 +3276,10 @@ NodePointer Demangler::demangleFunctionEntity() { Args = None; Kind = Node::Kind::PropertyWrapperBackingInitializer; break; + case 'W': + Args = None; + Kind = Node::Kind::PropertyWrapperInitFromProjectedValue; + break; default: return nullptr; } diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index d8c7e138fbe19..8d7bab3b3bdfd 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -419,6 +419,7 @@ class NodePrinter { case Node::Kind::InfixOperator: case Node::Kind::Initializer: case Node::Kind::PropertyWrapperBackingInitializer: + case Node::Kind::PropertyWrapperInitFromProjectedValue: case Node::Kind::KeyPathGetterThunkHelper: case Node::Kind::KeyPathSetterThunkHelper: case Node::Kind::KeyPathEqualsThunkHelper: @@ -1266,6 +1267,10 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { return printEntity( Node, asPrefixContext, TypePrinting::NoType, /*hasName*/false, "property wrapper backing initializer"); + case Node::Kind::PropertyWrapperInitFromProjectedValue: + return printEntity( + Node, asPrefixContext, TypePrinting::NoType, + /*hasName*/false, "property wrapper init from projected value"); case Node::Kind::DefaultArgumentInitializer: return printEntity(Node, asPrefixContext, TypePrinting::NoType, /*hasName*/false, "default argument ", @@ -2897,7 +2902,8 @@ printEntity(NodePointer Entity, bool asPrefixContext, TypePrinting TypePr, // Print any left over context which couldn't be printed in prefix form. if (Entity->getKind() == Node::Kind::DefaultArgumentInitializer || Entity->getKind() == Node::Kind::Initializer || - Entity->getKind() == Node::Kind::PropertyWrapperBackingInitializer) { + Entity->getKind() == Node::Kind::PropertyWrapperBackingInitializer || + Entity->getKind() == Node::Kind::PropertyWrapperInitFromProjectedValue) { Printer << " of "; } else { Printer << " in "; diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index c6dbab8dc6deb..f1ecb813d64bd 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -843,6 +843,11 @@ void Remangler::manglePropertyWrapperBackingInitializer(Node *node, mangleSimpleEntity(node, 'I', "P", ctx); } +void Remangler::manglePropertyWrapperInitFromProjectedValue(Node *node, + EntityContext &ctx) { + mangleSimpleEntity(node, 'I', "W", ctx); +} + void Remangler::mangleDefaultArgumentInitializer(Node *node, EntityContext &ctx) { mangleNamedEntity(node, 'I', "A", ctx); diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index 9d16388a1bd26..150e850b2be27 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -528,6 +528,7 @@ void Remangler::mangleGenericArgs(Node *node, char &Separator, case Node::Kind::DefaultArgumentInitializer: case Node::Kind::Initializer: case Node::Kind::PropertyWrapperBackingInitializer: + case Node::Kind::PropertyWrapperInitFromProjectedValue: if (!fullSubstitutionMap) break; @@ -1708,6 +1709,11 @@ void Remangler::manglePropertyWrapperBackingInitializer(Node *node) { Buffer << "fP"; } +void Remangler::manglePropertyWrapperInitFromProjectedValue(Node *node) { + mangleChildNodes(node); + Buffer << "fW"; +} + void Remangler::mangleLazyProtocolWitnessTableAccessor(Node *node) { mangleChildNodes(node); Buffer << "Wl"; @@ -2800,6 +2806,7 @@ bool Demangle::isSpecialized(Node *node) { case Node::Kind::ImplicitClosure: case Node::Kind::Initializer: case Node::Kind::PropertyWrapperBackingInitializer: + case Node::Kind::PropertyWrapperInitFromProjectedValue: case Node::Kind::DefaultArgumentInitializer: case Node::Kind::Getter: case Node::Kind::Setter: @@ -2840,6 +2847,7 @@ NodePointer Demangle::getUnspecialized(Node *node, NodeFactory &Factory) { case Node::Kind::ImplicitClosure: case Node::Kind::Initializer: case Node::Kind::PropertyWrapperBackingInitializer: + case Node::Kind::PropertyWrapperInitFromProjectedValue: case Node::Kind::DefaultArgumentInitializer: NumToCopy = node->getNumChildren(); LLVM_FALLTHROUGH; diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index ccce97e212283..8b872e03b95c5 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -1097,6 +1097,15 @@ class ExprContextAnalyzer { recordPossibleType(AFD->mapTypeIntoContext(param->getInterfaceType())); break; } + case InitializerKind::PropertyWrapper: { + auto initDC = cast(DC); + auto AFD = dyn_cast(initDC->getParent()); + if (!AFD) + return; + auto *param = initDC->getParam(); + recordPossibleType(AFD->mapTypeIntoContext(param->getInterfaceType())); + break; + } } } diff --git a/lib/IRGen/GenObjC.cpp b/lib/IRGen/GenObjC.cpp index fb8093370beb6..b2ad6325c2e11 100644 --- a/lib/IRGen/GenObjC.cpp +++ b/lib/IRGen/GenObjC.cpp @@ -472,6 +472,7 @@ namespace { case SILDeclRef::Kind::EnumElement: case SILDeclRef::Kind::GlobalAccessor: case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: llvm_unreachable("Method does not have a selector"); case SILDeclRef::Kind::Destroyer: diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 39250b5fe2f2d..9261ecb153f5f 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -2048,7 +2048,7 @@ void Parser::parseOptionalArgumentLabel(Identifier &name, SourceLoc &loc) { .fixItRemoveChars(end.getAdvancedLoc(-1), end); } - loc = consumeArgumentLabel(name); + loc = consumeArgumentLabel(name, /*diagnoseDollarPrefix=*/false); consumeToken(tok::colon); } } @@ -2599,7 +2599,7 @@ ParserStatus Parser::parseClosureSignatureIfPresent( Identifier name; SourceLoc nameLoc; if (Tok.is(tok::identifier)) { - nameLoc = consumeIdentifier(name, /*diagnoseDollarPrefix=*/true); + nameLoc = consumeIdentifier(name, /*diagnoseDollarPrefix=*/false); } else { nameLoc = consumeToken(tok::kw__); } @@ -3175,7 +3175,7 @@ Parser::parseTrailingClosures(bool isExprBasic, SourceRange calleeRange, SyntaxParsingContext ClosureCtx(SyntaxContext, SyntaxKind::MultipleTrailingClosureElement); Identifier label; - auto labelLoc = consumeArgumentLabel(label); + auto labelLoc = consumeArgumentLabel(label, /*diagnoseDollarPrefix=*/false); consumeToken(tok::colon); ParserResult closure; if (Tok.is(tok::l_brace)) { diff --git a/lib/Parse/ParsePattern.cpp b/lib/Parse/ParsePattern.cpp index 29776adef3704..be7bead1e3bbd 100644 --- a/lib/Parse/ParsePattern.cpp +++ b/lib/Parse/ParsePattern.cpp @@ -276,11 +276,13 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc, if (startsParameterName(*this, isClosure)) { // identifier-or-none for the first name - param.FirstNameLoc = consumeArgumentLabel(param.FirstName); + param.FirstNameLoc = consumeArgumentLabel(param.FirstName, + /*diagnoseDollarPrefix=*/!isClosure); // identifier-or-none? for the second name if (Tok.canBeArgumentLabel()) - param.SecondNameLoc = consumeArgumentLabel(param.SecondName); + param.SecondNameLoc = consumeArgumentLabel(param.SecondName, + /*diagnoseDollarPrefix=*/true); // Operators, closures, and enum elements cannot have API names. if ((paramContext == ParameterContextKind::Operator || diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index 87295871acc47..fbc4cb489a8b0 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -1020,11 +1020,13 @@ ParserResult Parser::parseTypeTupleBody() { && (peekToken().is(tok::colon) || peekToken().canBeArgumentLabel())) { // Consume a name. - element.NameLoc = consumeArgumentLabel(element.Name); + element.NameLoc = consumeArgumentLabel(element.Name, + /*diagnoseDollarPrefix=*/true); // If there is a second name, consume it as well. if (Tok.canBeArgumentLabel()) - element.SecondNameLoc = consumeArgumentLabel(element.SecondName); + element.SecondNameLoc = consumeArgumentLabel(element.SecondName, + /*diagnoseDollarPrefix=*/true); // Consume the ':'. if (consumeIf(tok::colon, element.ColonLoc)) { diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index 57f17157a811b..744c14ad83206 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -248,8 +248,15 @@ SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const { return forDefinition ? linkage : addExternalToLinkage(linkage); }; - // Function-local declarations have private linkage, unless serialized. ValueDecl *d = getDecl(); + + // Property wrapper generators of public functions have PublicNonABI linkage + if (isPropertyWrapperBackingInitializer() && isa(d)) { + if (isSerialized()) + return maybeAddExternal(SILLinkage::PublicNonABI); + } + + // Function-local declarations have private linkage, unless serialized. DeclContext *moduleContext = d->getDeclContext(); while (!moduleContext->isModuleScopeContext()) { if (moduleContext->isLocalContext()) { @@ -488,9 +495,16 @@ IsSerialized_t SILDeclRef::isSerialized() const { auto *d = getDecl(); - // Default argument generators are serialized if the containing - // declaration is public. - if (isDefaultArgGenerator()) { + // Default and property wrapper argument generators are serialized if the + // containing declaration is public. + if (isDefaultArgGenerator() || (isPropertyWrapperBackingInitializer() && + isa(d))) { + if (isPropertyWrapperBackingInitializer()) { + if (auto *func = dyn_cast_or_null(d->getDeclContext()->getAsDecl())) { + d = func; + } + } + // Ask the AST if we're inside an @inlinable context. if (d->getDeclContext()->getResilienceExpansion() == ResilienceExpansion::Minimal) { @@ -838,6 +852,10 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const { case SILDeclRef::Kind::PropertyWrapperBackingInitializer: return mangler.mangleBackingInitializerEntity(cast(getDecl()), SKind); + + case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: + return mangler.mangleInitFromProjectedValueEntity(cast(getDecl()), + SKind); } llvm_unreachable("bad entity kind!"); diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index d7604da0190df..0cf43b94483c8 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -2506,6 +2506,7 @@ static CanSILFunctionType getNativeSILFunctionType( case SILDeclRef::Kind::DefaultArgGenerator: case SILDeclRef::Kind::StoredPropertyInitializer: case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: case SILDeclRef::Kind::IVarInitializer: case SILDeclRef::Kind::IVarDestroyer: return getSILFunctionTypeForConventions( @@ -3038,6 +3039,7 @@ static ObjCSelectorFamily getObjCSelectorFamily(SILDeclRef c) { case SILDeclRef::Kind::DefaultArgGenerator: case SILDeclRef::Kind::StoredPropertyInitializer: case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: llvm_unreachable("Unexpected Kind of foreign SILDeclRef"); } @@ -3282,6 +3284,7 @@ TypeConverter::getDeclRefRepresentation(SILDeclRef c) { case SILDeclRef::Kind::DefaultArgGenerator: case SILDeclRef::Kind::StoredPropertyInitializer: case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: return SILFunctionTypeRepresentation::Thin; case SILDeclRef::Kind::Func: @@ -4121,6 +4124,7 @@ static AbstractFunctionDecl *getBridgedFunction(SILDeclRef declRef) { case SILDeclRef::Kind::DefaultArgGenerator: case SILDeclRef::Kind::StoredPropertyInitializer: case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: case SILDeclRef::Kind::IVarInitializer: case SILDeclRef::Kind::IVarDestroyer: return nullptr; diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 4f0e839d8a5cc..ca10ff4262926 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -344,6 +344,9 @@ void SILDeclRef::print(raw_ostream &OS) const { case SILDeclRef::Kind::PropertyWrapperBackingInitializer: OS << "!backinginit"; break; + case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: + OS << "!projectedvalueinit"; + break; } if (isForeign) diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index 059355a35417a..916aa7a22eab8 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -2456,6 +2456,28 @@ static CanAnyFunctionType getPropertyWrapperBackingInitializerInterfaceType( return CanAnyFunctionType::get(getCanonicalSignatureOrNull(sig), {param}, resultType); } + +static CanAnyFunctionType getPropertyWrapperInitFromProjectedValueInterfaceType(TypeConverter &TC, + VarDecl *VD) { + CanType resultType = + VD->getPropertyWrapperBackingPropertyType()->getCanonicalType(); + + Type interfaceType = VD->getPropertyWrapperProjectionVar()->getInterfaceType(); + if (interfaceType->hasArchetype()) + interfaceType = interfaceType->mapTypeOutOfContext(); + + CanType inputType = interfaceType->getCanonicalType(); + + auto *DC = VD->getInnermostDeclContext(); + auto sig = DC->getGenericSignatureOfContext(); + + AnyFunctionType::Param param( + inputType, Identifier(), + ParameterTypeFlags().withValueOwnership(ValueOwnership::Owned)); + return CanAnyFunctionType::get(getCanonicalSignatureOrNull(sig), {param}, + resultType); +} + /// Get the type of a destructor function. static CanAnyFunctionType getDestructorInterfaceType(DestructorDecl *dd, bool isDeallocating, @@ -2610,6 +2632,9 @@ CanAnyFunctionType TypeConverter::makeConstantInterfaceType(SILDeclRef c) { case SILDeclRef::Kind::PropertyWrapperBackingInitializer: return getPropertyWrapperBackingInitializerInterfaceType(*this, cast(vd)); + case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: + return getPropertyWrapperInitFromProjectedValueInterfaceType(*this, + cast(vd)); case SILDeclRef::Kind::IVarInitializer: return getIVarInitDestroyerInterfaceType(cast(vd), c.isForeign, false); @@ -2649,6 +2674,7 @@ TypeConverter::getConstantGenericSignature(SILDeclRef c) { case SILDeclRef::Kind::GlobalAccessor: case SILDeclRef::Kind::StoredPropertyInitializer: case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: return vd->getDeclContext()->getGenericSignatureOfContext(); } @@ -2731,6 +2757,7 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) { switch (fn.kind) { case SILDeclRef::Kind::StoredPropertyInitializer: case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: return CaptureInfo::empty(); default: diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index d2f46d00ffebd..f479bed23306d 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -1483,6 +1483,9 @@ bool SILParser::parseSILDeclRef(SILDeclRef &Result, } else if (!ParseState && Id.str() == "backinginit") { Kind = SILDeclRef::Kind::PropertyWrapperBackingInitializer; ParseState = 1; + } else if (!ParseState && Id.str() == "projectedvalueinit") { + Kind = SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue; + ParseState = 1; } else if (Id.str() == "foreign") { IsObjC = true; break; diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index c09b4ca0a4392..65153fe2f3f0d 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -900,8 +900,8 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) { ->isPropertyMemberwiseInitializedWithWrappedType()) { auto wrapperInfo = originalProperty->getPropertyWrapperBackingPropertyInfo(); - assert(wrapperInfo.wrappedValuePlaceholder->getOriginalWrappedValue()); - init = wrapperInfo.wrappedValuePlaceholder->getOriginalWrappedValue(); + assert(wrapperInfo.getWrappedValuePlaceholder()->getOriginalWrappedValue()); + init = wrapperInfo.getWrappedValuePlaceholder()->getOriginalWrappedValue(); } } @@ -934,12 +934,30 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) { PrettyStackTraceSILFunction X( "silgen emitPropertyWrapperBackingInitializer", f); auto wrapperInfo = var->getPropertyWrapperBackingPropertyInfo(); - assert(wrapperInfo.initializeFromOriginal); - f->createProfiler(wrapperInfo.initializeFromOriginal, constant, + assert(wrapperInfo.hasInitFromWrappedValue()); + f->createProfiler(wrapperInfo.getInitFromWrappedValue(), constant, ForDefinition); auto varDC = var->getInnermostDeclContext(); SILGenFunction SGF(*this, *f, varDC); - SGF.emitGeneratorFunction(constant, wrapperInfo.initializeFromOriginal); + SGF.emitGeneratorFunction(constant, wrapperInfo.getInitFromWrappedValue()); + postEmitFunction(constant, f); + break; + } + + case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: { + auto *var = cast(constant.getDecl()); + + auto loc = RegularLocation::getAutoGeneratedLocation(var); + preEmitFunction(constant, f, loc); + PrettyStackTraceSILFunction X( + "silgen emitPropertyWrapperInitFromProjectedValue", f); + auto wrapperInfo = var->getPropertyWrapperBackingPropertyInfo(); + assert(wrapperInfo.hasInitFromProjectedValue()); + f->createProfiler(wrapperInfo.getInitFromProjectedValue(), constant, + ForDefinition); + auto varDC = var->getInnermostDeclContext(); + SILGenFunction SGF(*this, *f, varDC); + SGF.emitGeneratorFunction(constant, wrapperInfo.getInitFromProjectedValue()); postEmitFunction(constant, f); break; } @@ -1477,8 +1495,17 @@ emitStoredPropertyInitialization(PatternBindingDecl *pbd, unsigned i) { void SILGenModule:: emitPropertyWrapperBackingInitializer(VarDecl *var) { - SILDeclRef constant(var, SILDeclRef::Kind::PropertyWrapperBackingInitializer); - emitOrDelayFunction(*this, constant); + auto wrapperInfo = var->getPropertyWrapperBackingPropertyInfo(); + + if (wrapperInfo.hasInitFromWrappedValue()) { + SILDeclRef constant(var, SILDeclRef::Kind::PropertyWrapperBackingInitializer); + emitOrDelayFunction(*this, constant); + } + + if (wrapperInfo.hasInitFromProjectedValue()) { + SILDeclRef constant(var, SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue); + emitOrDelayFunction(*this, constant); + } } SILFunction *SILGenModule::emitLazyGlobalInitializer(StringRef funcName, diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 595053150a593..8f5fb6652b518 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -5028,8 +5028,12 @@ RValue SILGenFunction::emitApplyOfPropertyWrapperBackingInitializer( VarDecl *var, SubstitutionMap subs, RValue &&originalValue, + SILDeclRef::Kind initKind, SGFContext C) { - SILDeclRef constant(var, SILDeclRef::Kind::PropertyWrapperBackingInitializer); + assert(initKind == SILDeclRef::Kind::PropertyWrapperBackingInitializer || + initKind == SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue); + + SILDeclRef constant(var, initKind); FormalEvaluationScope writebackScope(*this); diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 1900ddd08cc66..c33f0e89401c7 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -117,7 +117,7 @@ static RValue maybeEmitPropertyWrapperInitFromValue( return std::move(arg); auto wrapperInfo = originalProperty->getPropertyWrapperBackingPropertyInfo(); - if (!wrapperInfo || !wrapperInfo.initializeFromOriginal) + if (!wrapperInfo || !wrapperInfo.hasInitFromWrappedValue()) return std::move(arg); return SGF.emitApplyOfPropertyWrapperBackingInitializer(loc, originalProperty, @@ -246,7 +246,7 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, // the property wrapper backing initializer. if (auto *wrappedVar = field->getOriginalWrappedProperty()) { auto wrappedInfo = wrappedVar->getPropertyWrapperBackingPropertyInfo(); - auto *placeholder = wrappedInfo.wrappedValuePlaceholder; + auto *placeholder = wrappedInfo.getWrappedValuePlaceholder(); if (placeholder && placeholder->getOriginalWrappedValue()) { auto arg = SGF.emitRValue(placeholder->getOriginalWrappedValue()); maybeEmitPropertyWrapperInitFromValue(SGF, Loc, field, subs, @@ -291,7 +291,7 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, // memberwise initialized, use the original wrapped value if it exists. if (auto *wrappedVar = field->getOriginalWrappedProperty()) { auto wrappedInfo = wrappedVar->getPropertyWrapperBackingPropertyInfo(); - auto *placeholder = wrappedInfo.wrappedValuePlaceholder; + auto *placeholder = wrappedInfo.getWrappedValuePlaceholder(); if (placeholder && placeholder->getOriginalWrappedValue()) { init = placeholder->getOriginalWrappedValue(); } diff --git a/lib/SILGen/SILGenDecl.cpp b/lib/SILGen/SILGenDecl.cpp index 8f8cc02176d10..c8cd386d4fe65 100644 --- a/lib/SILGen/SILGenDecl.cpp +++ b/lib/SILGen/SILGenDecl.cpp @@ -1190,7 +1190,7 @@ void SILGenFunction::emitPatternBinding(PatternBindingDecl *PBD, if (var && var->getDeclContext()->isLocalContext()) { if (auto *orig = var->getOriginalWrappedProperty()) { auto wrapperInfo = orig->getPropertyWrapperBackingPropertyInfo(); - Init = wrapperInfo.wrappedValuePlaceholder->getOriginalWrappedValue(); + Init = wrapperInfo.getWrappedValuePlaceholder()->getOriginalWrappedValue(); auto value = emitRValue(Init); emitApplyOfPropertyWrapperBackingInitializer(SILLocation(PBD), orig, @@ -1225,7 +1225,7 @@ void SILGenFunction::visitVarDecl(VarDecl *D) { if (D->getAttrs().hasAttribute()) { // Emit the property wrapper backing initializer if necessary. auto wrapperInfo = D->getPropertyWrapperBackingPropertyInfo(); - if (wrapperInfo && wrapperInfo.initializeFromOriginal) + if (wrapperInfo && wrapperInfo.hasInitFromWrappedValue()) SGM.emitPropertyWrapperBackingInitializer(D); } diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 29ca7a958ff84..6682d6a636a65 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -517,6 +517,8 @@ namespace { RValue visitOpaqueValueExpr(OpaqueValueExpr *E, SGFContext C); RValue visitPropertyWrapperValuePlaceholderExpr( PropertyWrapperValuePlaceholderExpr *E, SGFContext C); + RValue visitAppliedPropertyWrapperExpr( + AppliedPropertyWrapperExpr *E, SGFContext C); RValue visitInOutToPointerExpr(InOutToPointerExpr *E, SGFContext C); RValue visitArrayToPointerExpr(ArrayToPointerExpr *E, SGFContext C); @@ -5232,6 +5234,29 @@ RValue RValueEmitter::visitPropertyWrapperValuePlaceholderExpr( return visitOpaqueValueExpr(E->getOpaqueValuePlaceholder(), C); } +RValue RValueEmitter::visitAppliedPropertyWrapperExpr( + AppliedPropertyWrapperExpr *E, SGFContext C) { + auto *param = const_cast(E->getParamDecl()); + auto argument = visit(E->getValue(), C); + SILDeclRef::Kind initKind; + switch (E->getValueKind()) { + case swift::AppliedPropertyWrapperExpr::ValueKind::WrappedValue: + initKind = SILDeclRef::Kind::PropertyWrapperBackingInitializer; + break; + case swift::AppliedPropertyWrapperExpr::ValueKind::ProjectedValue: + initKind = SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue; + break; + } + + SubstitutionMap subs; + if (param->getDeclContext()->getAsDecl()) { + subs = E->getCallee().getSubstitutions(); + } + + return SGF.emitApplyOfPropertyWrapperBackingInitializer( + SILLocation(E), param, subs, std::move(argument), initKind); +} + ProtocolDecl *SILGenFunction::getPointerProtocol() { if (SGM.PointerProtocol) return *SGM.PointerProtocol; diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 4c8dd14891328..d5a62e22f66f6 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -132,6 +132,8 @@ DeclName SILGenModule::getMagicFunctionName(SILDeclRef ref) { case SILDeclRef::Kind::StoredPropertyInitializer: case SILDeclRef::Kind::PropertyWrapperBackingInitializer: return getMagicFunctionName(cast(ref.getDecl())->getDeclContext()); + case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: + return getMagicFunctionName(cast(ref.getDecl())->getDeclContext()); case SILDeclRef::Kind::IVarInitializer: return getMagicFunctionName(cast(ref.getDecl())); case SILDeclRef::Kind::IVarDestroyer: @@ -516,6 +518,11 @@ void SILGenFunction::emitFunction(FuncDecl *fd) { emitProlog(captureInfo, fd->getParameters(), fd->getImplicitSelfDecl(), fd, fd->getResultInterfaceType(), fd->hasThrows(), fd->getThrowsLoc()); prepareEpilog(true, fd->hasThrows(), CleanupLocation(fd)); + for (auto *param : *fd->getParameters()) { + param->visitAuxiliaryDecls([&](VarDecl *auxiliaryVar) { + visit(auxiliaryVar); + }); + } if (fd->isAsyncHandler() && // If F.isAsync() we are emitting the asyncHandler body and not the @@ -594,6 +601,12 @@ void SILGenFunction::emitClosure(AbstractClosureExpr *ace) { emitProlog(captureInfo, ace->getParameters(), /*selfParam=*/nullptr, ace, resultIfaceTy, ace->isBodyThrowing(), ace->getLoc()); prepareEpilog(true, ace->isBodyThrowing(), CleanupLocation(ace)); + for (auto *param : *ace->getParameters()) { + param->visitAuxiliaryDecls([&](VarDecl *auxiliaryVar) { + visit(auxiliaryVar); + }); + } + if (auto *ce = dyn_cast(ace)) { emitStmt(ce->getBody()); } else { @@ -885,9 +898,10 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, Expr *value, } // For a property wrapper backing initializer, form a parameter list - // containing the wrapped value. + // containing the wrapped or projected value. ParameterList *params = nullptr; - if (function.kind == SILDeclRef::Kind::PropertyWrapperBackingInitializer) { + if (function.kind == SILDeclRef::Kind::PropertyWrapperBackingInitializer || + function.kind == SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue) { auto &ctx = getASTContext(); auto param = new (ctx) ParamDecl(SourceLoc(), SourceLoc(), ctx.getIdentifier("$input_value"), @@ -895,8 +909,18 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, Expr *value, ctx.getIdentifier("$input_value"), dc); param->setSpecifier(ParamSpecifier::Owned); + param->setImplicit(); auto vd = cast(function.getDecl()); - param->setInterfaceType(vd->getPropertyWrapperInitValueInterfaceType()); + if (function.kind == SILDeclRef::Kind::PropertyWrapperBackingInitializer) { + param->setInterfaceType(vd->getPropertyWrapperInitValueInterfaceType()); + } else { + auto *placeholder = vd->getPropertyWrapperBackingPropertyInfo().getProjectedValuePlaceholder(); + auto interfaceType = placeholder->getType(); + if (interfaceType->hasArchetype()) + interfaceType = interfaceType->mapTypeOutOfContext(); + + param->setInterfaceType(interfaceType); + } params = ParameterList::create(ctx, SourceLoc(), {param}, SourceLoc()); } @@ -918,12 +942,22 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, Expr *value, auto var = cast(function.getDecl()); auto wrappedInfo = var->getPropertyWrapperBackingPropertyInfo(); auto param = params->get(0); - auto *placeholder = wrappedInfo.wrappedValuePlaceholder; + auto *placeholder = wrappedInfo.getWrappedValuePlaceholder(); + opaqueValue.emplace( + *this, placeholder->getOpaqueValuePlaceholder(), + maybeEmitValueOfLocalVarDecl(param, AccessKind::Read)); + + assert(value == wrappedInfo.getInitFromWrappedValue()); + } else if (function.kind == SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue) { + auto var = cast(function.getDecl()); + auto wrappedInfo = var->getPropertyWrapperBackingPropertyInfo(); + auto param = params->get(0); + auto *placeholder = wrappedInfo.getProjectedValuePlaceholder(); opaqueValue.emplace( *this, placeholder->getOpaqueValuePlaceholder(), maybeEmitValueOfLocalVarDecl(param, AccessKind::Read)); - assert(value == wrappedInfo.initializeFromOriginal); + assert(value == wrappedInfo.getInitFromProjectedValue()); } emitReturnExpr(Loc, value); diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 5e2a0a323b4ea..e26bd9c964fec 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -1603,6 +1603,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction VarDecl *var, SubstitutionMap subs, RValue &&originalValue, + SILDeclRef::Kind initKind = SILDeclRef::Kind::PropertyWrapperBackingInitializer, SGFContext C = SGFContext()); /// A convenience method for emitApply that just handles monomorphic diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 1ac00ec0a9572..fe69b3d6032e9 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -1304,10 +1304,13 @@ namespace { bool canRewriteSetAsPropertyWrapperInit(SILGenFunction &SGF) const { if (auto *VD = dyn_cast(Storage)) { + if (VD->isImplicit() || isa(VD)) + return false; + // If this is not a wrapper property that can be initialized from // a value of the wrapped type, we can't perform the initialization. auto wrapperInfo = VD->getPropertyWrapperBackingPropertyInfo(); - if (!wrapperInfo.initializeFromOriginal) + if (!wrapperInfo.hasInitFromWrappedValue()) return false; bool isAssignmentToSelfParamInInit = @@ -1322,14 +1325,14 @@ namespace { // If this var isn't in a type context, assignment will always use the setter // if there is an initial value. if (!VD->getDeclContext()->isTypeContext() && - wrapperInfo.wrappedValuePlaceholder->getOriginalWrappedValue()) + wrapperInfo.getWrappedValuePlaceholder()->getOriginalWrappedValue()) return false; // If this property wrapper uses autoclosure in it's initializer, // the argument types of the setter and initializer shall be // different, so we don't rewrite an assignment into an // initialization. - return !wrapperInfo.wrappedValuePlaceholder->isAutoClosure(); + return !wrapperInfo.getWrappedValuePlaceholder()->isAutoClosure(); } return false; diff --git a/lib/SILGen/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index 970f45ba5088a..b6a4cd6ad0e9f 100644 --- a/lib/SILGen/SILGenProlog.cpp +++ b/lib/SILGen/SILGenProlog.cpp @@ -19,6 +19,7 @@ #include "swift/AST/CanTypeVisitor.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/ParameterList.h" +#include "swift/AST/PropertyWrappers.h" using namespace swift; using namespace Lowering; @@ -242,6 +243,14 @@ struct ArgumentInitHelper { } void emitParam(ParamDecl *PD) { + if (auto wrapperInfo = PD->getPropertyWrapperBackingPropertyInfo()) { + if (wrapperInfo.hasSynthesizedInitializers()) { + SGF.SGM.emitPropertyWrapperBackingInitializer(PD); + } + + PD = cast(wrapperInfo.backingVar); + } + auto type = PD->getType(); assert(type->isMaterializable()); diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index 5cac871a7bce1..18c26e660075b 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -1135,7 +1135,7 @@ class SILGenType : public TypeMemberVisitor { // If this variable has an attached property wrapper with an initialization // function, emit the backing initializer function. if (auto wrapperInfo = vd->getPropertyWrapperBackingPropertyInfo()) { - if (wrapperInfo.initializeFromOriginal && !vd->isStatic()) { + if (wrapperInfo.hasInitFromWrappedValue() && !vd->isStatic()) { SGM.emitPropertyWrapperBackingInitializer(vd); } } diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index d3c5bcb27eabf..06f3bbb17dd44 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -671,7 +671,19 @@ namespace { new (ctx) DeclRefExpr(ref, loc, implicit, semantics, fullType); cs.cacheType(declRefExpr); declRefExpr->setFunctionRefKind(choice.getFunctionRefKind()); - return forceUnwrapIfExpected(declRefExpr, choice, locator); + Expr *result = forceUnwrapIfExpected(declRefExpr, choice, locator); + + if (auto *fnDecl = dyn_cast(decl)) { + if (AnyFunctionRef(fnDecl).hasPropertyWrapperParameters() && + (declRefExpr->getFunctionRefKind() == FunctionRefKind::Compound || + declRefExpr->getFunctionRefKind() == FunctionRefKind::Unapplied)) { + auto &appliedWrappers = solution.appliedPropertyWrappers[locator.getAnchor()]; + result = buildPropertyWrapperFnThunk(result, fullType->getAs(), fnDecl, + ref, appliedWrappers); + } + } + + return result; } /// Describes an opened existential that has not yet been closed. @@ -965,6 +977,103 @@ namespace { return false; } + AutoClosureExpr *buildPropertyWrapperFnThunk( + Expr *fnRef, FunctionType *fnType, AnyFunctionRef fnDecl, ConcreteDeclRef ref, + ArrayRef appliedPropertyWrappers) { + auto &context = cs.getASTContext(); + auto paramInfo = fnType->getParams(); + + SmallVector args; + SmallVector argLabels; + + auto *innerParams = fnDecl.getParameters(); + SmallVector innerParamTypes; + + OptionSet options + = (ParameterList::Implicit | + ParameterList::NamedArguments); + auto *outerParams = innerParams->clone(context, options); + SmallVector outerParamTypes; + + unsigned appliedWrapperIndex = 0; + for (auto i : indices(*innerParams)) { + auto *innerParam = innerParams->get(i); + auto *outerParam = outerParams->get(i); + auto outerParamType = paramInfo[i].getPlainType(); + + Expr *paramRef = new (context) DeclRefExpr(outerParam, DeclNameLoc(), + /*implicit=*/true); + paramRef->setType(outerParam->isInOut() + ? LValueType::get(outerParamType) : outerParamType); + cs.cacheType(paramRef); + + if (auto wrapperInfo = innerParam->getPropertyWrapperBackingPropertyInfo()) { + // Rewrite the parameter ref to the backing wrapper initialization + // expression. + auto appliedWrapper = appliedPropertyWrappers[appliedWrapperIndex++]; + auto wrapperType = appliedWrapper.wrapperType; + auto initKind = appliedWrapper.initKind; + + using ValueKind = AppliedPropertyWrapperExpr::ValueKind; + ValueKind valueKind = (initKind == PropertyWrapperInitKind::ProjectedValue ? + ValueKind::ProjectedValue : ValueKind::WrappedValue); + + paramRef = AppliedPropertyWrapperExpr::create(context, ref, innerParam, + innerParam->getStartLoc(), + wrapperType, paramRef, valueKind); + cs.cacheExprTypes(paramRef); + + // SILGen knows how to emit property-wrapped parameters, but the + // function type needs the backing wrapper type in its param list. + innerParamTypes.push_back(AnyFunctionType::Param(paramRef->getType(), + innerParam->getArgumentName())); + } else { + // Rewrite the parameter ref if necessary. + if (outerParam->isInOut()) { + paramRef = new (context) InOutExpr(SourceLoc(), paramRef, + outerParamType, /*implicit=*/true); + } else if (innerParam->isVariadic()) { + paramRef = new (context) VarargExpansionExpr(paramRef, + /*implicit=*/ true, + outerParamType); + } + cs.cacheType(paramRef); + + innerParamTypes.push_back(innerParam->toFunctionParam()); + } + + // The outer parameters are for an autoclosure, which shouldn't have + // argument labels. + outerParamTypes.push_back(AnyFunctionType::Param(outerParamType, + Identifier(), + paramInfo[i].getParameterFlags())); + outerParam->setInterfaceType(outerParamType); + + if (fnDecl.getAbstractFunctionDecl()) + argLabels.push_back(innerParam->getArgumentName()); + + args.push_back(paramRef); + } + + fnRef->setType(FunctionType::get(innerParamTypes, fnType->getResult())); + cs.cacheType(fnRef); + + auto *fnCall = CallExpr::createImplicit(context, fnRef, args, argLabels); + fnCall->setType(fnType->getResult()); + cs.cacheType(fnCall); + cs.cacheType(fnCall->getArg()); + + auto discriminator = AutoClosureExpr::InvalidDiscriminator; + auto *autoClosureType = FunctionType::get(outerParamTypes, fnType->getResult()); + auto *autoClosure = new (context) AutoClosureExpr(fnCall, autoClosureType, + discriminator, cs.DC); + autoClosure->setParameterList(outerParams); + autoClosure->setThunkKind(AutoClosureExpr::Kind::SingleCurryThunk); + cs.cacheType(autoClosure); + + return autoClosure; + } + AutoClosureExpr *buildCurryThunk(ValueDecl *member, FunctionType *selfFnTy, Expr *selfParamRef, @@ -999,8 +1108,9 @@ namespace { cs.cacheType(closure); auto refTy = cs.getType(ref)->castTo(); - auto calleeParams = refTy->getResult()->castTo()->getParams(); - auto calleeResultTy = refTy->getResult()->castTo()->getResult(); + auto calleeFnType = refTy->getResult()->castTo(); + auto calleeParams = calleeFnType->getParams(); + auto calleeResultTy = calleeFnType->getResult(); auto selfParam = refTy->getParams()[0]; auto selfParamTy = selfParam.getPlainType(); @@ -1047,6 +1157,20 @@ namespace { if (selfParamRef->isSuperExpr()) selfCall->setIsSuper(true); + auto &appliedWrappers = solution.appliedPropertyWrappers[locator.getAnchor()]; + if (!appliedWrappers.empty()) { + auto fnDecl = AnyFunctionRef(dyn_cast(member)); + auto callee = resolveConcreteDeclRef(member, locator); + auto *closure = buildPropertyWrapperFnThunk(selfCall, calleeFnType, + fnDecl, callee, appliedWrappers); + + ref->setType(FunctionType::get(refTy->getParams(), selfCall->getType())); + cs.cacheType(ref); + + // FIXME: There's more work to do. + return closure; + } + // Pass all the closure parameters to the call. SmallVector labels; SmallVector labelLocs; @@ -1712,7 +1836,8 @@ namespace { coerceCallArguments(Expr *arg, AnyFunctionType *funcType, ConcreteDeclRef callee, ApplyExpr *apply, ArrayRef argLabels, - ConstraintLocatorBuilder locator); + ConstraintLocatorBuilder locator, + ArrayRef appliedPropertyWrappers); /// Coerce the given 'self' argument (e.g., for the base of a /// member expression) to the given type. @@ -1918,10 +2043,12 @@ namespace { ->castTo(); auto fullSubscriptTy = openedFullFnType->getResult() ->castTo(); + auto &appliedWrappers = solution.appliedPropertyWrappers[memberLoc.getAnchor()]; index = coerceCallArguments(index, fullSubscriptTy, subscriptRef, nullptr, argLabels, locator.withPathElement( - ConstraintLocator::ApplyArgument)); + ConstraintLocator::ApplyArgument), + appliedWrappers); if (!index) return nullptr; @@ -2743,7 +2870,8 @@ namespace { auto newArg = coerceCallArguments( expr->getArg(), fnType, witness, /*applyExpr=*/nullptr, expr->getArgumentLabels(), - cs.getConstraintLocator(expr, ConstraintLocator::ApplyArgument)); + cs.getConstraintLocator(expr, ConstraintLocator::ApplyArgument), + /*appliedPropertyWrappers=*/{}); expr->setInitializer(witness); expr->setArg(newArg); @@ -3437,9 +3565,19 @@ namespace { Expr *visitPropertyWrapperValuePlaceholderExpr( PropertyWrapperValuePlaceholderExpr *expr) { + // If there is no opaque value placeholder, the enclosing init(wrappedValue:) + // expression cannot be reused, so we only need the original wrapped value + // argument in the AST. + if (!expr->getOpaqueValuePlaceholder()) + return expr->getOriginalWrappedValue(); + return expr; } + Expr *visitAppliedPropertyWrapperExpr(AppliedPropertyWrapperExpr *expr) { + llvm_unreachable("Already type-checked"); + } + Expr *visitDefaultArgumentExpr(DefaultArgumentExpr *expr) { llvm_unreachable("Already type-checked"); } @@ -4925,7 +5063,7 @@ namespace { auto *newIndexExpr = coerceCallArguments(indexExpr, subscriptType, ref, /*applyExpr*/ nullptr, labels, - locator); + locator, /*appliedPropertyWrappers*/ {}); // We need to be able to hash the captured index values in order for // KeyPath itself to be hashable, so check that all of the subscript @@ -5461,9 +5599,11 @@ Expr *ExprRewriter::coerceCallArguments( ConcreteDeclRef callee, ApplyExpr *apply, ArrayRef argLabels, - ConstraintLocatorBuilder locator) { + ConstraintLocatorBuilder locator, + ArrayRef appliedPropertyWrappers) { auto &ctx = getConstraintSystem().getASTContext(); auto params = funcType->getParams(); + unsigned appliedWrapperIndex = 0; // Local function to produce a locator to refer to the given parameter. auto getArgLocator = @@ -5693,6 +5833,20 @@ Expr *ExprRewriter::coerceCallArguments( return true; }; + if (auto *param = paramInfo.getPropertyWrapperParam(paramIdx)) { + auto appliedWrapper = appliedPropertyWrappers[appliedWrapperIndex++]; + auto wrapperType = solution.simplifyType(appliedWrapper.wrapperType); + auto initKind = appliedWrapper.initKind; + + using ValueKind = AppliedPropertyWrapperExpr::ValueKind; + ValueKind valueKind = (initKind == PropertyWrapperInitKind::ProjectedValue ? + ValueKind::ProjectedValue : ValueKind::WrappedValue); + + arg = AppliedPropertyWrapperExpr::create(ctx, callee, param, arg->getStartLoc(), + wrapperType, arg, valueKind); + cs.cacheExprTypes(arg); + } + if (argRequiresAutoClosureExpr(param, argType)) { assert(!param.isInOut()); @@ -7130,12 +7284,14 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, SmallVector argLabelsScratch; + auto &appliedWrappers = solution.appliedPropertyWrappers[calleeLocator.getAnchor()]; auto fnType = cs.getType(fn)->getAs(); arg = coerceCallArguments(arg, fnType, declRef, apply, apply->getArgumentLabels(argLabelsScratch), locator.withPathElement( - ConstraintLocator::ApplyArgument)); + ConstraintLocator::ApplyArgument), + appliedWrappers); if (!arg) { return nullptr; } @@ -7335,10 +7491,12 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, // the function. SmallVector argLabelsScratch; if (auto fnType = cs.getType(fn)->getAs()) { + auto &appliedWrappers = solution.appliedPropertyWrappers[calleeLocator.getAnchor()]; arg = coerceCallArguments(arg, fnType, callee, apply, apply->getArgumentLabels(argLabelsScratch), locator.withPathElement( - ConstraintLocator::ApplyArgument)); + ConstraintLocator::ApplyArgument), + appliedWrappers); if (!arg) { return nullptr; } @@ -7607,6 +7765,12 @@ namespace { public: ExprWalker(ExprRewriter &Rewriter) : Rewriter(Rewriter) { } + bool shouldWalkIntoPropertyWrapperPlaceholderValue() override { + // Property wrapper placeholder underlying values are filled in + // with already-type-checked expressions. Don't walk into them. + return false; + } + const SmallVectorImpl &getClosuresToTypeCheck() const { return ClosuresToTypeCheck; } @@ -7619,6 +7783,11 @@ namespace { // For closures, update the parameter types and check the body. if (auto closure = dyn_cast(expr)) { rewriteFunction(closure); + + if (AnyFunctionRef(closure).hasPropertyWrapperParameters()) { + return { false, rewriteClosure(closure) }; + } + return { false, closure }; } @@ -7655,6 +7824,38 @@ namespace { Optional rewriteTarget(SolutionApplicationTarget target); + AutoClosureExpr *rewriteClosure(ClosureExpr *closure) { + auto &solution = Rewriter.solution; + auto closureType = solution.simplifyType(solution.getType(closure)); + FunctionType *closureFnType = closureType->castTo(); + + // Apply types to synthesized property wrapper vars. + for (auto *param : *closure->getParameters()) { + if (!param->hasAttachedPropertyWrapper()) + continue; + + // Set the interface type of each property wrapper synthesized var + auto *backingVar = param->getPropertyWrapperBackingProperty(); + backingVar->setInterfaceType( + solution.simplifyType(solution.getType(backingVar))->mapTypeOutOfContext()); + + if (auto *projectionVar = param->getPropertyWrapperProjectionVar()) { + projectionVar->setInterfaceType( + solution.simplifyType(solution.getType(projectionVar))); + } + + auto *wrappedValueVar = param->getPropertyWrapperWrappedValueVar(); + wrappedValueVar->setInterfaceType( + solution.simplifyType(solution.getType(wrappedValueVar))); + } + + TypeChecker::checkParameterList(closure->getParameters(), closure); + + auto &appliedWrappers = Rewriter.solution.appliedPropertyWrappers[closure]; + return Rewriter.buildPropertyWrapperFnThunk(closure, closureFnType, closure, + ConcreteDeclRef(), appliedWrappers); + } + /// Rewrite the function for the given solution. /// /// \returns true if an error occurred. @@ -8203,7 +8404,7 @@ ExprWalker::rewriteTarget(SolutionApplicationTarget target) { // Get the outermost wrapper type from the solution auto outermostWrapper = wrappedVar->getAttachedPropertyWrappers().front(); auto backingType = solution.simplifyType( - solution.getType(outermostWrapper->getTypeRepr())); + solution.getType(outermostWrapper->getTypeExpr())); auto &ctx = solution.getConstraintSystem().getASTContext(); ctx.setSideCachedPropertyWrapperBackingPropertyType( diff --git a/lib/Sema/CSClosure.cpp b/lib/Sema/CSClosure.cpp index 3c4c7f06932fb..365f372fcb9e4 100644 --- a/lib/Sema/CSClosure.cpp +++ b/lib/Sema/CSClosure.cpp @@ -305,7 +305,7 @@ SolutionApplicationToFunctionResult ConstraintSystem::applySolution( // Coerce the parameter types. closureFnType = closureType->castTo(); auto *params = closure->getParameters(); - TypeChecker::coerceParameterListToType(params, closure, closureFnType); + TypeChecker::coerceParameterListToType(params, closureFnType); // Coerce the result type, if it was written explicitly. if (closure->hasExplicitResultType()) { diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 06ccaf96e3c64..45b0d382c7313 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -3291,12 +3291,37 @@ bool MissingPropertyWrapperUnwrapFailure::diagnoseAsError() { return true; } + if (isa(getProperty())) { + auto wrapperType = getToType(); + auto wrappedValueType = computeWrappedValueType(getProperty(), wrapperType); + emitDiagnostic(diag::property_wrapper_param_projection_invalid, wrappedValueType); + return true; + } + emitDiagnostic(diag::incorrect_property_wrapper_reference, getPropertyName(), getFromType(), getToType(), true) .fixItRemoveChars(getLoc(), endLoc); return true; } +bool MissingProjectedValueFailure::diagnoseAsError() { + emitDiagnostic(diag::property_wrapper_param_no_projection, wrapperType); + return true; +} + +bool MissingPropertyWrapperAttributeFailure::diagnoseAsError() { + if (auto *param = getAsDecl(getAnchor())) { + emitDiagnostic(diag::invalid_implicit_property_wrapper, wrapperType); + + // FIXME: emit a note and fix-it to add '@propertyWrapper' if the + // type is a nominal and in the same module. + } else { + emitDiagnostic(diag::property_wrapper_param_no_wrapper); + } + + return true; +} + bool SubscriptMisuseFailure::diagnoseAsError() { auto *locator = getLocator(); auto &sourceMgr = getASTContext().SourceMgr; diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 45097fe251a2e..abbf26ecf5fa7 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1068,6 +1068,28 @@ class MissingPropertyWrapperUnwrapFailure final bool diagnoseAsError() override; }; +class MissingProjectedValueFailure final : public FailureDiagnostic { + Type wrapperType; + +public: + MissingProjectedValueFailure(const Solution &solution, Type wrapper, + ConstraintLocator *locator) + : FailureDiagnostic(solution, locator), wrapperType(resolveType(wrapper)) {} + + bool diagnoseAsError() override; +}; + +class MissingPropertyWrapperAttributeFailure final : public FailureDiagnostic { + Type wrapperType; + +public: + MissingPropertyWrapperAttributeFailure(const Solution &solution, Type wrapper, + ConstraintLocator *locator) + : FailureDiagnostic(solution, locator), wrapperType(resolveType(wrapper)) {} + + bool diagnoseAsError() override; +}; + class SubscriptMisuseFailure final : public FailureDiagnostic { public: SubscriptMisuseFailure(const Solution &solution, ConstraintLocator *locator) diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index 8d2b22c0c7aa5..02ccf27666273 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -629,6 +629,27 @@ UseWrappedValue *UseWrappedValue::create(ConstraintSystem &cs, UseWrappedValue(cs, propertyWrapper, base, wrapper, locator); } +bool AddProjectedValue::diagnose(const Solution &solution, bool asNote) const { + MissingProjectedValueFailure failure(solution, wrapperType, getLocator()); + return failure.diagnose(asNote); +} + +AddProjectedValue *AddProjectedValue::create(ConstraintSystem &cs, Type wrapperType, + ConstraintLocator *locator) { + return new (cs.getAllocator()) AddProjectedValue(cs, wrapperType, locator); +} + +bool AddPropertyWrapperAttribute::diagnose(const Solution &solution, bool asNote) const { + MissingPropertyWrapperAttributeFailure failure(solution, wrapperType, getLocator()); + return failure.diagnose(asNote); +} + +AddPropertyWrapperAttribute *AddPropertyWrapperAttribute::create(ConstraintSystem &cs, + Type wrapperType, + ConstraintLocator *locator) { + return new (cs.getAllocator()) AddPropertyWrapperAttribute(cs, wrapperType, locator); +} + bool UseSubscriptOperator::diagnose(const Solution &solution, bool asNote) const { SubscriptMisuseFailure failure(solution, getLocator()); diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 14628286925d5..158f0033094f4 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1975,7 +1975,7 @@ namespace { if (auto *paramList = closure->getParameters()) { for (unsigned i = 0, n = paramList->size(); i != n; ++i) { - const auto *param = paramList->get(i); + auto *param = paramList->get(i); auto *paramLoc = CS.getConstraintLocator(closure, LocatorPathElt::TupleElement(i)); @@ -2605,6 +2605,16 @@ namespace { Type visitPropertyWrapperValuePlaceholderExpr( PropertyWrapperValuePlaceholderExpr *expr) { + if (auto ty = expr->getType()) { + CS.cacheType(expr); + return ty; + } + + assert(CS.getType(expr)); + return CS.getType(expr); + } + + Type visitAppliedPropertyWrapperExpr(AppliedPropertyWrapperExpr *expr) { return expr->getType(); } @@ -3556,8 +3566,8 @@ static Expr *generateConstraintsFor(ConstraintSystem &cs, Expr *expr, /// \param wrappedVar The property that has a property wrapper. /// \returns the type of the property. static bool generateWrappedPropertyTypeConstraints( - ConstraintSystem &cs, Type initializerType, VarDecl *wrappedVar, - Type propertyType) { + ConstraintSystem &cs, Type initializerType, VarDecl *wrappedVar, + Type propertyType) { auto dc = wrappedVar->getInnermostDeclContext(); Type wrapperType = LValueType::get(initializerType); @@ -3889,10 +3899,11 @@ bool ConstraintSystem::generateConstraints( case SolutionApplicationTarget::Kind::uninitializedWrappedVar: { auto *wrappedVar = target.getAsUninitializedWrappedVar(); auto *outermostWrapper = wrappedVar->getAttachedPropertyWrappers().front(); - auto *typeRepr = outermostWrapper->getTypeRepr(); + auto *typeExpr = outermostWrapper->getTypeExpr(); auto backingType = replaceInferableTypesWithTypeVars( - outermostWrapper->getType(),getConstraintLocator(typeRepr)); - setType(typeRepr, backingType); + outermostWrapper->getType(),getConstraintLocator(typeExpr)); + + setType(typeExpr, backingType); auto propertyType = getVarType(wrappedVar); if (propertyType->hasError()) @@ -4041,6 +4052,67 @@ bool ConstraintSystem::generateConstraints( return false; } +ConstraintSystem::TypeMatchResult +ConstraintSystem::applyPropertyWrapperToParameter( + Type wrapperType, Type paramType, ParamDecl *param, Identifier argLabel, + ConstraintKind matchKind, ConstraintLocatorBuilder locator) { + Expr *anchor = getAsExpr(locator.getAnchor()); + if (auto *apply = dyn_cast(anchor)) { + anchor = apply->getFn(); + } + + if (argLabel.hasDollarPrefix() && (!param || !param->hasAttachedPropertyWrapper())) { + if (!shouldAttemptFixes()) + return getTypeMatchFailure(locator); + + addConstraint(matchKind, paramType, wrapperType, locator); + auto *fix = AddPropertyWrapperAttribute::create(*this, wrapperType, getConstraintLocator(locator)); + if (recordFix(fix)) + return getTypeMatchFailure(locator); + + return getTypeMatchSuccess(); + } + + PropertyWrapperInitKind initKind; + if (argLabel.hasDollarPrefix()) { + auto attemptProjectedValueFix = [&](ConstraintFix *fix) -> ConstraintSystem::TypeMatchResult { + if (shouldAttemptFixes()) { + if (paramType->hasTypeVariable()) + recordPotentialHole(paramType); + + if (!recordFix(fix)) + return getTypeMatchSuccess(); + } + + return getTypeMatchFailure(locator); + }; + + auto typeInfo = wrapperType->getAnyNominal()->getPropertyWrapperTypeInfo(); + if (!typeInfo.projectedValueVar) { + auto *fix = AddProjectedValue::create(*this, wrapperType, getConstraintLocator(locator)); + return attemptProjectedValueFix(fix); + } + + Type projectionType = computeProjectedValueType(param, wrapperType); + addConstraint(matchKind, paramType, projectionType, locator); + + if (!param->hasImplicitPropertyWrapper() && + param->getAttachedPropertyWrappers().front()->getArg()) { + auto *fix = UseWrappedValue::create(*this, param, /*base=*/Type(), wrapperType, + getConstraintLocator(locator)); + return attemptProjectedValueFix(fix); + } + + initKind = PropertyWrapperInitKind::ProjectedValue; + } else { + generateWrappedPropertyTypeConstraints(*this, wrapperType, param, paramType); + initKind = PropertyWrapperInitKind::WrappedValue; + } + + appliedPropertyWrappers[anchor].push_back({ wrapperType, initKind }); + return getTypeMatchSuccess(); +} + void ConstraintSystem::optimizeConstraints(Expr *e) { if (getASTContext().TypeCheckerOpts.DisableConstraintSolverPerformanceHacks) return; diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 62fcb75179a06..edd089bd5b923 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -289,10 +289,13 @@ static bool matchCallArgumentsImpl( assert(argNumber != numArgs && "Must have a valid index to claim"); assert(!claimedArgs[argNumber] && "Argument already claimed"); + auto argLabel = args[argNumber].getLabel(); if (!actualArgNames.empty()) { // We're recording argument names; record this one. actualArgNames[argNumber] = expectedName; - } else if (args[argNumber].getLabel() != expectedName && !ignoreNameClash) { + } else if (argLabel != expectedName && !ignoreNameClash && + !(argLabel.str().startswith("$") && + argLabel.str().drop_front() == expectedName.str())) { // We have an argument name mismatch. Start recording argument names. actualArgNames.resize(numArgs); @@ -340,7 +343,9 @@ static bool matchCallArgumentsImpl( for (unsigned i = nextArgIdx; i != numArgs; ++i) { auto argLabel = args[i].getLabel(); - if (argLabel != paramLabel) { + if (argLabel != paramLabel && + !(argLabel.str().startswith("$") && + argLabel.str().drop_front() == paramLabel.str())) { // If this is an attempt to claim additional unlabeled arguments // for variadic parameter, we have to stop at first labeled argument. if (forVariadic) @@ -1415,6 +1420,16 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( cs, cs.getConstraintLocator(loc))); } + auto *wrappedParam = paramInfo.getPropertyWrapperParam(argIdx); + auto argLabel = argument.getLabel(); + if (wrappedParam || argLabel.hasDollarPrefix()) { + if (cs.applyPropertyWrapperToParameter(paramTy, argTy, const_cast(wrappedParam), + argLabel, subKind, locator).isFailure()) { + return cs.getTypeMatchFailure(loc); + } + continue; + } + // If argument comes for declaration it should loose // `@autoclosure` flag, because in context it's used // as a function type represented by autoclosure. @@ -6686,8 +6701,27 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName, } } + DeclNameRef lookupName = memberName; + if (memberName.isCompoundName()) { + auto &context = getASTContext(); + + // Remove any $ prefixes for lookup + SmallVector lookupLabels; + for (auto label : memberName.getArgumentNames()) { + if (label.hasDollarPrefix()) { + auto unprefixed = label.str().drop_front(); + lookupLabels.push_back(context.getIdentifier(unprefixed)); + } else { + lookupLabels.push_back(label); + } + } + + DeclName unprefixedName(context, memberName.getBaseName(), lookupLabels); + lookupName = DeclNameRef(unprefixedName); + } + // Look for members within the base. - LookupResult &lookup = lookupMember(instanceTy, memberName); + LookupResult &lookup = lookupMember(instanceTy, lookupName); // If this is true, we're using type construction syntax (Foo()) rather // than an explicit call to `init` (Foo.init()). @@ -8155,6 +8189,7 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, SmallVector parameters; for (unsigned i = 0, n = paramList->size(); i != n; ++i) { auto param = inferredClosureType->getParams()[i]; + auto *paramDecl = paramList->get(i); // In case of anonymous parameters let's infer flags from context // that helps to infer variadic and inout earlier. @@ -8163,8 +8198,56 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, param = param.withFlags(contextualParam->getParameterFlags()); } + if (paramDecl->hasAttachedPropertyWrapper()) { + bool hasError = false; + Type backingType; + + if (paramDecl->hasImplicitPropertyWrapper()) { + backingType = getContextualParamAt(i)->getPlainType(); + + // If the contextual type is not a property wrapper, record a fix. + auto *nominal = backingType->getAnyNominal(); + if (!(nominal && nominal->getAttrs().hasAttribute())) { + if (!shouldAttemptFixes()) + return false; + + paramDecl->visitAuxiliaryDecls([](VarDecl *var) { + var->setInvalid(); + }); + + auto *fix = AddPropertyWrapperAttribute::create(*this, backingType, + getConstraintLocator(paramDecl)); + recordFix(fix); + hasError = true; + } + } else { + auto *wrapperAttr = paramDecl->getAttachedPropertyWrappers().front(); + auto wrapperType = paramDecl->getAttachedPropertyWrapperType(0); + backingType = replaceInferableTypesWithTypeVars( + wrapperType, getConstraintLocator(wrapperAttr->getTypeRepr())); + } + + if (!hasError) { + auto result = applyPropertyWrapperToParameter(backingType, param.getParameterType(), + paramDecl, paramDecl->getName(), + ConstraintKind::Equal, locator); + if (result.isFailure()) + return false; + + auto *backingVar = paramDecl->getPropertyWrapperBackingProperty(); + setType(backingVar, backingType); + + auto *localWrappedVar = paramDecl->getPropertyWrapperWrappedValueVar(); + setType(localWrappedVar, computeWrappedValueType(paramDecl, backingType)); + + if (auto *projection = paramDecl->getPropertyWrapperProjectionVar()) { + setType(projection, computeProjectedValueType(paramDecl, backingType)); + } + } + } + Type internalType; - if (paramList->get(i)->getTypeRepr()) { + if (paramDecl->getTypeRepr()) { // Internal type is the type used in the body of the closure, // so "external" type translates to it as follows: // - `Int...` -> `[Int]`, @@ -8205,7 +8288,7 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, } } - setType(paramList->get(i), internalType); + setType(paramDecl, internalType); parameters.push_back(param); } @@ -10630,6 +10713,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( case FixKind::SkipUnhandledConstructInResultBuilder: case FixKind::UsePropertyWrapper: case FixKind::UseWrappedValue: + case FixKind::AddProjectedValue: + case FixKind::AddPropertyWrapperAttribute: case FixKind::ExpandArrayIntoVarargs: case FixKind::UseRawValue: case FixKind::ExplicitlyConstructRawRepresentable: diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 257f63fb5e5b7..7786d36c63a63 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -189,6 +189,10 @@ Solution ConstraintSystem::finalize() { solution.resultBuilderTransformed.insert(transformed); } + for (const auto &appliedWrapper : appliedPropertyWrappers) { + solution.appliedPropertyWrappers.insert(appliedWrapper); + } + return solution; } @@ -273,7 +277,11 @@ void ConstraintSystem::applySolution(const Solution &solution) { for (const auto &transformed : solution.resultBuilderTransformed) { resultBuilderTransformed.push_back(transformed); } - + + for (const auto &appliedWrapper : solution.appliedPropertyWrappers) { + appliedPropertyWrappers.insert(appliedWrapper); + } + // Register any fixes produced along this path. Fixes.append(solution.Fixes.begin(), solution.Fixes.end()); } @@ -474,6 +482,7 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs) numDisabledConstraints = cs.solverState->getNumDisabledConstraints(); numFavoredConstraints = cs.solverState->getNumFavoredConstraints(); numResultBuilderTransformed = cs.resultBuilderTransformed.size(); + numAppliedPropertyWrappers = cs.appliedPropertyWrappers.size(); numResolvedOverloads = cs.ResolvedOverloads.size(); numInferredClosureTypes = cs.ClosureTypes.size(); numContextualTypes = cs.contextualTypes.size(); @@ -554,6 +563,9 @@ ConstraintSystem::SolverScope::~SolverScope() { /// Remove any builder transformed closures. truncate(cs.resultBuilderTransformed, numResultBuilderTransformed); + // Remove any applied property wrappers. + truncate(cs.appliedPropertyWrappers, numAppliedPropertyWrappers); + // Remove any inferred closure types (e.g. used in result builder body). truncate(cs.ClosureTypes, numInferredClosureTypes); diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index aca323b2e4891..2e6d4c0629db1 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -266,7 +266,7 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, varInterfaceType = var->getPropertyWrapperInitValueInterfaceType(); auto wrapperInfo = var->getPropertyWrapperBackingPropertyInfo(); - isAutoClosure = wrapperInfo.wrappedValuePlaceholder->isAutoClosure(); + isAutoClosure = wrapperInfo.getWrappedValuePlaceholder()->isAutoClosure(); } else { varInterfaceType = backingPropertyType; } diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 8a285537ea981..7e5cd9172eae3 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -1156,6 +1156,61 @@ static unsigned getNumRemovedArgumentLabels(ValueDecl *decl, llvm_unreachable("Unhandled FunctionRefKind in switch."); } +/// Replaces property wrapper types in the parameter list of the given function type +/// with the wrapped-value or projected-value types (depending on argument label). +static FunctionType * +unwrapPropertyWrapperParameterTypes(ConstraintSystem &cs, AbstractFunctionDecl *funcDecl, + FunctionRefKind functionRefKind, FunctionType *functionType, + ConstraintLocatorBuilder locator) { + // Only apply property wrappers to unapplied references to functions. + if (!(functionRefKind == FunctionRefKind::Compound || + functionRefKind == FunctionRefKind::Unapplied)) { + return functionType; + } + + auto *paramList = funcDecl->getParameters(); + auto paramTypes = functionType->getParams(); + SmallVector adjustedParamTypes; + + DeclNameLoc nameLoc; + auto *ref = getAsExpr(locator.getAnchor()); + if (auto *declRef = dyn_cast(ref)) { + nameLoc = declRef->getNameLoc(); + } else if (auto *dotExpr = dyn_cast(ref)) { + nameLoc = dotExpr->getNameLoc(); + } else if (auto *overloadedRef = dyn_cast(ref)) { + nameLoc = overloadedRef->getNameLoc(); + } else if (auto *memberExpr = dyn_cast(ref)) { + nameLoc = memberExpr->getNameLoc(); + } + + for (unsigned i : indices(*paramList)) { + Identifier argLabel; + if (functionRefKind == FunctionRefKind::Compound) { + auto &context = cs.getASTContext(); + auto argLabelLoc = nameLoc.getArgumentLabelLoc(i); + auto argLabelToken = Lexer::getTokenAtLocation(context.SourceMgr, argLabelLoc); + argLabel = context.getIdentifier(argLabelToken.getText()); + } + + auto *paramDecl = paramList->get(i); + if (!paramDecl->hasAttachedPropertyWrapper() && !argLabel.hasDollarPrefix()) { + adjustedParamTypes.push_back(paramTypes[i]); + continue; + } + + auto *wrappedType = cs.createTypeVariable(cs.getConstraintLocator(locator), 0); + auto paramType = paramTypes[i].getParameterType(); + auto paramLabel = paramTypes[i].getLabel(); + adjustedParamTypes.push_back(AnyFunctionType::Param(wrappedType, paramLabel)); + cs.applyPropertyWrapperToParameter(paramType, wrappedType, paramDecl, argLabel, + ConstraintKind::Equal, locator); + } + + return FunctionType::get(adjustedParamTypes, functionType->getResult(), + functionType->getExtInfo()); +} + std::pair ConstraintSystem::getTypeOfReference(ValueDecl *value, FunctionRefKind functionRefKind, @@ -1201,6 +1256,10 @@ ConstraintSystem::getTypeOfReference(ValueDecl *value, funcDecl->getDeclContext()) ->removeArgumentLabels(numLabelsToRemove); + openedType = unwrapPropertyWrapperParameterTypes(*this, funcDecl, functionRefKind, + openedType->getAs(), + locator); + // If we opened up any type variables, record the replacements. recordOpenedTypes(locator, replacements); @@ -1631,6 +1690,16 @@ ConstraintSystem::getTypeOfMemberReference( [&](Type type) { return openType(type, replacements); }); } + if (auto *funcDecl = dyn_cast(value)) { + auto *fullFunctionType = openedType->getAs(); + + // Strip off the 'self' parameter + auto *functionType = fullFunctionType->getResult()->getAs(); + functionType = unwrapPropertyWrapperParameterTypes(*this, funcDecl, functionRefKind, + functionType, locator); + openedType = FunctionType::get(fullFunctionType->getParams(), functionType); + } + // Compute the type of the reference. Type type = openedType; @@ -4907,8 +4976,8 @@ void SolutionApplicationTarget::maybeApplyPropertyWrapper() { expression.propertyWrapper.hasInitialWrappedValue = true; } // Form init(wrappedValue:) call(s). - Expr *wrappedInitializer = buildPropertyWrapperWrappedValueCall( - singleVar, Type(), initializer, /*ignoreAttributeArgs=*/false, + Expr *wrappedInitializer = buildPropertyWrapperInitCall( + singleVar, Type(), initializer, PropertyWrapperInitKind::WrappedValue, [&](ApplyExpr *innermostInit) { expression.propertyWrapper.innermostWrappedValueInit = innermostInit; }); diff --git a/lib/Sema/PreCheckExpr.cpp b/lib/Sema/PreCheckExpr.cpp index c2d4cec546546..132e932229cb8 100644 --- a/lib/Sema/PreCheckExpr.cpp +++ b/lib/Sema/PreCheckExpr.cpp @@ -338,6 +338,25 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclNameRef Name = UDRE->getName(); SourceLoc Loc = UDRE->getLoc(); + DeclNameRef LookupName = Name; + if (Name.isCompoundName()) { + auto &context = DC->getASTContext(); + + // Remove any $ prefixes for lookup + SmallVector lookupLabels; + for (auto label : Name.getArgumentNames()) { + if (label.hasDollarPrefix()) { + auto unprefixed = label.str().drop_front(); + lookupLabels.push_back(context.getIdentifier(unprefixed)); + } else { + lookupLabels.push_back(label); + } + } + + DeclName lookupName(context, Name.getBaseName(), lookupLabels); + LookupName = DeclNameRef(lookupName); + } + auto errorResult = [&]() -> Expr * { if (replaceInvalidRefsWithErrors) return new (DC->getASTContext()) ErrorExpr(UDRE->getSourceRange()); @@ -362,7 +381,7 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, if (Loc.isValid() && !Name.isOperator()) { SmallVector localDecls; ASTScope::lookupLocalDecls(DC->getParentSourceFile(), - Name.getFullName(), Loc, + LookupName.getFullName(), Loc, /*stopAfterInnermostBraceStmt=*/false, ResultValues); for (auto *localDecl : ResultValues) { @@ -376,7 +395,7 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, if (Loc.isInvalid()) DC = DC->getModuleScopeContext(); - Lookup = TypeChecker::lookupUnqualified(DC, Name, Loc, lookupOptions); + Lookup = TypeChecker::lookupUnqualified(DC, LookupName, Loc, lookupOptions); ValueDecl *localDeclAfterUse = nullptr; auto isValid = [&](ValueDecl *D) { @@ -435,7 +454,7 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, NameLookupOptions relookupOptions = lookupOptions; relookupOptions |= NameLookupFlags::IgnoreAccessControl; auto inaccessibleResults = - TypeChecker::lookupUnqualified(DC, Name, Loc, relookupOptions); + TypeChecker::lookupUnqualified(DC, LookupName, Loc, relookupOptions); if (inaccessibleResults) { // FIXME: What if the unviable candidates have different levels of access? const ValueDecl *first = inaccessibleResults.front().getValueDecl(); diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 659d875e6727e..f8169124bd785 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2987,7 +2987,7 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { // through a property. if (nominal->getAttrs().hasAttribute()) { // property wrappers can only be applied to variables - if (!isa(D) || isa(D)) { + if (!isa(D)) { diagnose(attr->getLocation(), diag::property_wrapper_attribute_not_on_property, nominal->getName()); @@ -2995,6 +2995,18 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { return; } + if (isa(D)) { + // Check for unsupported declarations. + auto *context = D->getDeclContext()->getAsDecl(); + if (context && isa(context)) { + diagnose(attr->getLocation(), + diag::property_wrapper_param_not_supported, + context->getDescriptiveKind()); + attr->setInvalid(); + return; + } + } + return; } diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 8f73bb82533d9..23b0cb091ed23 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1401,36 +1401,6 @@ static void maybeDiagnoseClassWithoutInitializers(ClassDecl *classDecl) { diagnoseClassWithoutInitializers(classDecl); } -void TypeChecker::checkParameterList(ParameterList *params, - DeclContext *owner) { - for (auto param: *params) { - checkDeclAttributes(param); - - // async autoclosures can only occur as parameters to async functions. - if (param->isAutoClosure()) { - if (auto fnType = param->getInterfaceType()->getAs()) { - if (fnType->isAsync() && - !(isa(owner) && - cast(owner)->isAsyncContext())) { - param->diagnose(diag::async_autoclosure_nonasync_function); - if (auto func = dyn_cast(owner)) - addAsyncNotes(func); - } - } - } - } - - // For source compatibilty, allow duplicate internal parameter names - // on protocol requirements. - // - // FIXME: Consider turning this into a warning or error if we do - // another -swift-version. - if (!isa(owner->getParent())) { - // Check for duplicate parameter names. - diagnoseDuplicateDecls(*params); - } -} - void TypeChecker::diagnoseDuplicateBoundVars(Pattern *pattern) { SmallVector boundVars; pattern->collectVariables(boundVars); @@ -2984,3 +2954,39 @@ void TypeChecker::typeCheckDecl(Decl *D) { auto *SF = D->getDeclContext()->getParentSourceFile(); DeclChecker(D->getASTContext(), SF).visit(D); } + +void TypeChecker::checkParameterList(ParameterList *params, + DeclContext *owner) { + for (auto param: *params) { + checkDeclAttributes(param); + + // async autoclosures can only occur as parameters to async functions. + if (param->isAutoClosure()) { + if (auto fnType = param->getInterfaceType()->getAs()) { + if (fnType->isAsync() && + !(isa(owner) && + cast(owner)->isAsyncContext())) { + param->diagnose(diag::async_autoclosure_nonasync_function); + if (auto func = dyn_cast(owner)) + addAsyncNotes(func); + } + } + } + + auto *SF = param->getDeclContext()->getParentSourceFile(); + param->visitAuxiliaryDecls([&](VarDecl *auxiliaryDecl) { + if (!isa(auxiliaryDecl)) + DeclChecker(param->getASTContext(), SF).visitBoundVariable(auxiliaryDecl); + }); + } + + // For source compatibilty, allow duplicate internal parameter names + // on protocol requirements. + // + // FIXME: Consider turning this into a warning or error if we do + // another -swift-version. + if (!isa(owner->getParent())) { + // Check for duplicate parameter names. + diagnoseDuplicateDecls(*params); + } +} diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index 3250c4d16b3d8..61cc4fa340704 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -1164,6 +1164,9 @@ class Context { /// A default argument expression. DefaultArgument, + /// A property wrapper initialization expression. + PropertyWrapper, + /// The initializer for an instance variable. IVarInitializer, @@ -1291,6 +1294,10 @@ class Context { return Context(Kind::DefaultArgument); } + if (isa(init)) { + return Context(Kind::PropertyWrapper); + } + auto *binding = cast(init)->getBinding(); assert(!binding->getDeclContext()->isLocalContext() && "setting up error context for local pattern binding?"); @@ -1539,6 +1546,7 @@ class Context { case Kind::GlobalVarInitializer: case Kind::IVarInitializer: case Kind::DefaultArgument: + case Kind::PropertyWrapper: case Kind::CatchPattern: case Kind::CatchGuard: case Kind::DeferBody: @@ -1563,6 +1571,7 @@ class Context { case Kind::GlobalVarInitializer: case Kind::IVarInitializer: case Kind::DefaultArgument: + case Kind::PropertyWrapper: case Kind::CatchPattern: case Kind::CatchGuard: case Kind::DeferBody: @@ -1680,6 +1689,7 @@ class Context { case Kind::GlobalVarInitializer: case Kind::IVarInitializer: case Kind::DefaultArgument: + case Kind::PropertyWrapper: case Kind::CatchPattern: case Kind::CatchGuard: case Kind::DeferBody: diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index d87aaedff1bae..4e1e98e7ccf48 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -24,6 +24,7 @@ #include "swift/AST/SourceFile.h" #include "swift/AST/NameLookup.h" #include "swift/AST/ParameterList.h" +#include "swift/AST/PropertyWrappers.h" #include "swift/AST/TypeCheckRequests.h" #include "llvm/Support/SaveAndRestore.h" #include @@ -1664,7 +1665,7 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, /// Coerce the specified parameter list of a ClosureExpr to the specified /// contextual type. -void TypeChecker::coerceParameterListToType(ParameterList *P, ClosureExpr *CE, +void TypeChecker::coerceParameterListToType(ParameterList *P, AnyFunctionType *FN) { // Local function to check if the given type is valid e.g. doesn't have diff --git a/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index 36db5a9203da4..d0db912396b71 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -25,17 +25,6 @@ #include "swift/AST/TypeCheckRequests.h" using namespace swift; -/// The kind of property initializer to look for -enum class PropertyWrapperInitKind { - /// An initial-value initializer (i.e. `init(initialValue:)`), which is - /// deprecated. - InitialValue, - /// An wrapped-value initializer (i.e. `init(wrappedValue:)`) - WrappedValue, - /// An default-value initializer (i.e. `init()` or `init(defaultArgs...)`) - Default -}; - static bool isDeclNotAsAccessibleAsParent(ValueDecl *decl, NominalTypeDecl *parent) { return decl->getFormalAccess() < @@ -120,6 +109,9 @@ findSuitableWrapperInit(ASTContext &ctx, NominalTypeDecl *nominal, case PropertyWrapperInitKind::WrappedValue: argumentLabel = ctx.Id_wrappedValue; break; + case PropertyWrapperInitKind::ProjectedValue: + argumentLabel = ctx.Id_projectedValue; + break; case PropertyWrapperInitKind::Default: break; } @@ -176,7 +168,8 @@ findSuitableWrapperInit(ASTContext &ctx, NominalTypeDecl *nominal, } // Additional checks for initial-value and wrapped-value initializers - if (initKind != PropertyWrapperInitKind::Default) { + if (initKind == PropertyWrapperInitKind::WrappedValue || + initKind == PropertyWrapperInitKind::InitialValue) { auto paramType = argumentParam->getInterfaceType(); if (paramType->is()) continue; @@ -346,6 +339,12 @@ PropertyWrapperTypeInfoRequest::evaluate( result.projectedValueVar = findValueProperty(ctx, nominal, ctx.Id_projectedValue, /*allowMissing=*/true); + if (result.projectedValueVar && + findSuitableWrapperInit(ctx, nominal, result.projectedValueVar, + PropertyWrapperInitKind::ProjectedValue, decls)) { + result.hasProjectedValueInit = true; + } + result.enclosingInstanceWrappedSubscript = findEnclosingSelfSubscript(ctx, nominal, ctx.Id_wrapped); result.enclosingInstanceProjectedSubscript = @@ -421,14 +420,14 @@ AttachedPropertyWrappersRequest::evaluate(Evaluator &evaluator, // Check that the variable is part of a single-variable pattern. auto binding = var->getParentPatternBinding(); - if (!binding || binding->getSingleVar() != var) { + if (binding && binding->getSingleVar() != var) { ctx.Diags.diagnose(attr->getLocation(), diag::property_wrapper_not_single_var); continue; } // A property wrapper cannot be attached to a 'let'. - if (var->isLet()) { + if (!isa(var) && var->isLet()) { ctx.Diags.diagnose(attr->getLocation(), diag::property_wrapper_let); continue; } @@ -456,6 +455,10 @@ AttachedPropertyWrappersRequest::evaluate(Evaluator &evaluator, continue; } + if (isa(var) && isa(dc)) { + dc = dc->getAsDecl()->getDeclContext(); + } + // A property with a wrapper cannot be declared in a protocol, enum, or // an extension. if (isa(dc) || @@ -469,7 +472,7 @@ AttachedPropertyWrappersRequest::evaluate(Evaluator &evaluator, else whichKind = 2; var->diagnose(diag::property_with_wrapper_in_bad_context, - var->getName(), whichKind) + var->getName(), whichKind, var->getDescriptiveKind()) .highlight(attr->getRange()); continue; @@ -523,6 +526,9 @@ Type AttachedPropertyWrapperTypeRequest::evaluate(Evaluator &evaluator, Type PropertyWrapperBackingPropertyTypeRequest::evaluate( Evaluator &evaluator, VarDecl *var) const { + if (var->hasImplicitPropertyWrapper()) + return var->getInterfaceType(); + Type rawType = evaluateOrDefault(evaluator, AttachedPropertyWrapperTypeRequest{var, 0}, Type()); @@ -530,14 +536,15 @@ PropertyWrapperBackingPropertyTypeRequest::evaluate( if (!rawType || rawType->hasError()) return Type(); - auto binding = var->getParentPatternBinding(); - if (!binding) + // The constraint system will infer closure parameter types + if (isa(var) && var->getInterfaceType()->hasError()) return Type(); // If there's an initializer of some sort, checking it will determine the // property wrapper type. - unsigned index = binding->getPatternEntryIndexForVarDecl(var); - if (binding->isInitialized(index)) { + auto binding = var->getParentPatternBinding(); + unsigned index = binding ? binding->getPatternEntryIndexForVarDecl(var) : 0; + if (binding && binding->isInitialized(index)) { // FIXME(InterfaceTypeRequest): Remove this. (void)var->getInterfaceType(); if (!binding->isInitializerChecked(index)) @@ -561,18 +568,22 @@ PropertyWrapperBackingPropertyTypeRequest::evaluate( return type; } -Type swift::computeWrappedValueType(VarDecl *var, Type backingStorageType, +Type swift::computeWrappedValueType(const VarDecl *var, Type backingStorageType, Optional limit) { auto wrapperAttrs = var->getAttachedPropertyWrappers(); - unsigned realLimit = wrapperAttrs.size(); + unsigned realLimit = var->hasImplicitPropertyWrapper() ? 1 : wrapperAttrs.size(); if (limit) realLimit = std::min(*limit, realLimit); // Follow the chain of wrapped value properties. Type wrappedValueType = backingStorageType; DeclContext *dc = var->getDeclContext(); - for (unsigned i : range(realLimit)) { - auto wrappedInfo = var->getAttachedPropertyWrapperTypeInfo(i); + while (realLimit--) { + auto *nominal = wrappedValueType->getDesugaredType()->getAnyNominal(); + if (!nominal) + return Type(); + + auto wrappedInfo = nominal->getPropertyWrapperTypeInfo(); if (!wrappedInfo) return wrappedValueType; @@ -587,8 +598,22 @@ Type swift::computeWrappedValueType(VarDecl *var, Type backingStorageType, return wrappedValueType; } -Expr *swift::buildPropertyWrapperWrappedValueCall( - VarDecl *var, Type backingStorageType, Expr *value, bool ignoreAttributeArgs, +Type swift::computeProjectedValueType(const VarDecl *var, Type backingStorageType) { + if (!var->hasAttachedPropertyWrapper()) + return Type(); + + if (var->hasImplicitPropertyWrapper()) + return backingStorageType; + + DeclContext *dc = var->getDeclContext(); + auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(0); + return backingStorageType->getTypeOfMember(dc->getParentModule(), + wrapperInfo.projectedValueVar); +} + +Expr *swift::buildPropertyWrapperInitCall( + const VarDecl *var, Type backingStorageType, Expr *value, + PropertyWrapperInitKind initKind, llvm::function_ref innermostInitCallback) { // From the innermost wrapper type out, form init(wrapperValue:) calls. ASTContext &ctx = var->getASTContext(); @@ -596,6 +621,18 @@ Expr *swift::buildPropertyWrapperWrappedValueCall( Expr *initializer = value; ApplyExpr *innermostInit = nullptr; + // Projected-value initializers don't compose, so no need to iterate + // over the wrapper attributes. + if (initKind == PropertyWrapperInitKind::ProjectedValue) { + auto typeExpr = TypeExpr::createImplicit(backingStorageType, ctx); + auto argName = ctx.Id_projectedValue; + auto *init = + CallExpr::createImplicit(ctx, typeExpr, { initializer }, { argName }); + + innermostInitCallback(init); + return init; + } + for (unsigned i : llvm::reverse(indices(wrapperAttrs))) { Type wrapperType = backingStorageType ? computeWrappedValueType(var, backingStorageType, i) @@ -616,8 +653,9 @@ Expr *swift::buildPropertyWrapperWrappedValueCall( // If there were no arguments provided for the attribute at this level, // call `init(wrappedValue:)` directly. auto attr = wrapperAttrs[i]; - if (!attr->getArg() || ignoreAttributeArgs) { + if (!attr->getArg()) { Identifier argName; + assert(initKind == PropertyWrapperInitKind::WrappedValue); switch (var->getAttachedPropertyWrapperTypeInfo(i).wrappedValueInit) { case PropertyWrapperTypeInfo::HasInitialValueInit: argName = ctx.Id_initialValue; diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 96c0cbb8f090c..3ea939cd33d76 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -40,6 +40,7 @@ #include "swift/Basic/TopCollection.h" #include "swift/Parse/Lexer.h" #include "swift/Parse/LocalContext.h" +#include "swift/Parse/Parser.h" #include "swift/Sema/IDETypeChecking.h" #include "swift/Syntax/TokenKinds.h" #include "llvm/ADT/DenseMap.h" diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 5e07b72fa7cc9..7b9c4c56e83bd 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -2439,14 +2439,19 @@ static VarDecl *synthesizePropertyWrapperProjectionVar( // Compute the name of the storage type. SmallString<64> nameBuf; - nameBuf = "$"; - nameBuf += var->getName().str(); + if (var->getName().hasDollarPrefix()) { + nameBuf = var->getName().str(); + } else { + nameBuf = "$"; + nameBuf += var->getName().str(); + } Identifier name = ctx.getIdentifier(nameBuf); // Determine the type of the property. - Type propertyType = wrapperType->getTypeOfMember( - var->getModuleContext(), wrapperVar, - wrapperVar->getValueInterfaceType()); + Type propertyType; + if (wrapperType) + propertyType = wrapperType->getTypeOfMember(var->getModuleContext(), wrapperVar, + wrapperVar->getValueInterfaceType()); // Form the property. auto dc = var->getDeclContext(); @@ -2454,7 +2459,8 @@ static VarDecl *synthesizePropertyWrapperProjectionVar( VarDecl::Introducer::Var, var->getLoc(), name, dc); - property->setInterfaceType(propertyType); + if (propertyType) + property->setInterfaceType(propertyType); property->setImplicit(); property->setOriginalWrappedProperty(var); addMemberToContextIfNeeded(property, dc, var); @@ -2476,16 +2482,23 @@ static VarDecl *synthesizePropertyWrapperProjectionVar( property->overwriteSetterAccess(var->getSetterFormalAccess()); // Add the accessors we need. - bool hasSetter = wrapperVar->isSettable(nullptr) && - wrapperVar->isSetterAccessibleFrom(var->getInnermostDeclContext()); - if (hasSetter) - property->setImplInfo(StorageImplInfo::getMutableComputed()); - else + if (var->hasImplicitPropertyWrapper()) { + // FIXME: This can have a setter, but we need a resolved type first + // to figure it out. property->setImplInfo(StorageImplInfo::getImmutableComputed()); + } else { + bool hasSetter = wrapperVar->isSettable(nullptr) && + wrapperVar->isSetterAccessibleFrom(var->getInnermostDeclContext()); + if (hasSetter) + property->setImplInfo(StorageImplInfo::getMutableComputed()); + else + property->setImplInfo(StorageImplInfo::getImmutableComputed()); + } - var->getAttrs().add( - new (ctx) ProjectedValuePropertyAttr(name, SourceLoc(), SourceRange(), - /*Implicit=*/true)); + if (!isa(var)) + var->getAttrs().add( + new (ctx) ProjectedValuePropertyAttr(name, SourceLoc(), SourceRange(), + /*Implicit=*/true)); return property; } @@ -2684,102 +2697,155 @@ PropertyWrapperLValuenessRequest::evaluate(Evaluator &, PropertyWrapperBackingPropertyInfo PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator, VarDecl *var) const { - // Determine the type of the backing property. - auto wrapperType = var->getPropertyWrapperBackingPropertyType(); - if (!wrapperType || wrapperType->hasError()) + if (!var->hasAttachedPropertyWrapper()) return PropertyWrapperBackingPropertyInfo(); auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(0); - if (!wrapperInfo) - return PropertyWrapperBackingPropertyInfo(); // Compute the name of the storage type. ASTContext &ctx = var->getASTContext(); SmallString<64> nameBuf; nameBuf = "_"; - nameBuf += var->getName().str(); + if (var->getName().hasDollarPrefix()) + nameBuf += var->getName().str().drop_front(); + else + nameBuf += var->getName().str(); Identifier name = ctx.getIdentifier(nameBuf); - // Determine the type of the storage. auto dc = var->getDeclContext(); + VarDecl *backingVar = nullptr; + VarDecl *projectionVar = nullptr; + + if (auto *param = dyn_cast(var)) { + backingVar = ParamDecl::cloneWithoutType(ctx, param); + backingVar->setName(name); + Type wrapperType; + + // If this is a function parameter, compute the backing + // type now. For closure parameters, let the constraint + // system infer the backing type. + if (!var->getInterfaceType()->hasError()) { + wrapperType = var->getPropertyWrapperBackingPropertyType(); + if (!wrapperType || wrapperType->hasError()) + return PropertyWrapperBackingPropertyInfo(); + + backingVar->setInterfaceType(wrapperType); + } + + if (wrapperInfo.projectedValueVar || var->getName().hasDollarPrefix()) { + projectionVar = + synthesizePropertyWrapperProjectionVar(ctx, var, wrapperType, + wrapperInfo.projectedValueVar); + } + } + + if (!wrapperInfo) + return PropertyWrapperBackingPropertyInfo(backingVar, projectionVar); + + // Determine the type of the storage. + auto wrapperType = var->getPropertyWrapperBackingPropertyType(); + if (!wrapperType || wrapperType->hasError()) + return PropertyWrapperBackingPropertyInfo(backingVar, projectionVar); + Type storageInterfaceType = wrapperType; Type storageType = dc->mapTypeIntoContext(storageInterfaceType); // Create the backing storage property and note it in the cache. - VarDecl *backingVar = new (ctx) VarDecl(/*IsStatic=*/var->isStatic(), - VarDecl::Introducer::Var, - var->getLoc(), - name, dc); - backingVar->setInterfaceType(storageInterfaceType); - backingVar->setImplicit(); - backingVar->setOriginalWrappedProperty(var); - - // The backing storage is 'private'. - backingVar->overwriteAccess(AccessLevel::Private); - backingVar->overwriteSetterAccess(AccessLevel::Private); - - addMemberToContextIfNeeded(backingVar, dc, var); - - // Create the pattern binding declaration for the backing property. - Pattern *pbdPattern = NamedPattern::createImplicit(ctx, backingVar); - pbdPattern->setType(storageType); - pbdPattern = TypedPattern::createImplicit(ctx, pbdPattern, storageType); - auto pbd = PatternBindingDecl::createImplicit( - ctx, var->getCorrectStaticSpelling(), pbdPattern, - /*init*/ nullptr, dc, SourceLoc()); - addMemberToContextIfNeeded(pbd, dc, var); - pbd->setStatic(var->isStatic()); - - // Take the initializer from the original property. - auto parentPBD = var->getParentPatternBinding(); - unsigned patternNumber = parentPBD->getPatternEntryIndexForVarDecl(var); - - // Force the default initializer to come into existence, if there is one, - // and the wrapper doesn't provide its own. - if (!parentPBD->isInitialized(patternNumber) - && parentPBD->isDefaultInitializable(patternNumber) - && !wrapperInfo.defaultInit) { - auto ty = parentPBD->getPattern(patternNumber)->getType(); - if (auto defaultInit = TypeChecker::buildDefaultInitializer(ty)) - parentPBD->setInit(patternNumber, defaultInit); - } - - if (parentPBD->isInitialized(patternNumber) && - !parentPBD->isInitializerChecked(patternNumber)) { - TypeChecker::typeCheckPatternBinding(parentPBD, patternNumber); + PatternBindingDecl *pbd = nullptr; + if (!backingVar) { + backingVar = new (ctx) VarDecl(/*IsStatic=*/var->isStatic(), + VarDecl::Introducer::Var, + var->getLoc(), + name, dc); + backingVar->setInterfaceType(storageInterfaceType); + backingVar->setImplicit(); + backingVar->setOriginalWrappedProperty(var); + + // The backing storage is 'private'. + backingVar->overwriteAccess(AccessLevel::Private); + backingVar->overwriteSetterAccess(AccessLevel::Private); + + addMemberToContextIfNeeded(backingVar, dc, var); + + // Create the pattern binding declaration for the backing property. + Pattern *pbdPattern = NamedPattern::createImplicit(ctx, backingVar); + pbdPattern->setType(storageType); + pbdPattern = TypedPattern::createImplicit(ctx, pbdPattern, storageType); + pbd = PatternBindingDecl::createImplicit(ctx, var->getCorrectStaticSpelling(), pbdPattern, + /*init*/ nullptr, dc, SourceLoc()); + addMemberToContextIfNeeded(pbd, dc, var); + pbd->setStatic(var->isStatic()); } Expr *initializer = nullptr; PropertyWrapperValuePlaceholderExpr *wrappedValue = nullptr; - if ((initializer = parentPBD->getInit(patternNumber))) { - pbd->setInit(0, initializer); - pbd->setInitializerChecked(0); - wrappedValue = findWrappedValuePlaceholder(initializer); - } else { - if (!parentPBD->isInitialized(patternNumber) && wrapperInfo.defaultInit) { - // FIXME: Record this expression somewhere so that DI can perform the - // initialization itself. - Expr *initializer = nullptr; - typeCheckSynthesizedWrapperInitializer(pbd, backingVar, parentPBD, - initializer); + // Take the initializer from the original property. + if (!isa(var)) { + auto parentPBD = var->getParentPatternBinding(); + unsigned patternNumber = parentPBD->getPatternEntryIndexForVarDecl(var); + + // Force the default initializer to come into existence, if there is one, + // and the wrapper doesn't provide its own. + if (!parentPBD->isInitialized(patternNumber) + && parentPBD->isDefaultInitializable(patternNumber) + && !wrapperInfo.defaultInit) { + auto ty = parentPBD->getPattern(patternNumber)->getType(); + if (auto defaultInit = TypeChecker::buildDefaultInitializer(ty)) + parentPBD->setInit(patternNumber, defaultInit); + } + + if (parentPBD->isInitialized(patternNumber) && + !parentPBD->isInitializerChecked(patternNumber)) { + TypeChecker::typeCheckPatternBinding(parentPBD, patternNumber); + } + + if ((initializer = parentPBD->getInit(patternNumber))) { pbd->setInit(0, initializer); pbd->setInitializerChecked(0); - } else if (var->hasObservers() && !dc->isTypeContext()) { - var->diagnose(diag::observingprop_requires_initializer); - } + wrappedValue = findWrappedValuePlaceholder(initializer); + } else { + if (!parentPBD->isInitialized(patternNumber) && wrapperInfo.defaultInit) { + // FIXME: Record this expression somewhere so that DI can perform the + // initialization itself. + Expr *initializer = nullptr; + typeCheckSynthesizedWrapperInitializer(pbd, backingVar, parentPBD, + initializer); + pbd->setInit(0, initializer); + pbd->setInitializerChecked(0); + } else if (var->hasObservers() && !dc->isTypeContext()) { + var->diagnose(diag::observingprop_requires_initializer); + } - if (var->getOpaqueResultTypeDecl()) { - var->diagnose(diag::opaque_type_var_no_underlying_type); + if (var->getOpaqueResultTypeDecl()) { + var->diagnose(diag::opaque_type_var_no_underlying_type); + } } } // If there is a projection property (projectedValue) in the wrapper, // synthesize a computed property for '$foo'. - VarDecl *storageVar = nullptr; + Expr *projectedValueInit = nullptr; if (wrapperInfo.projectedValueVar) { - storageVar = synthesizePropertyWrapperProjectionVar( - ctx, var, storageInterfaceType, wrapperInfo.projectedValueVar); + if (!projectionVar) { + projectionVar = synthesizePropertyWrapperProjectionVar(ctx, var, storageInterfaceType, + wrapperInfo.projectedValueVar); + } + + // Projected-value initialization is currently only supported for parameters. + if (wrapperInfo.hasProjectedValueInit && isa(var)) { + auto *param = dyn_cast(var); + auto *placeholder = PropertyWrapperValuePlaceholderExpr::create( + ctx, var->getSourceRange(), projectionVar->getType(), /*projectedValue=*/nullptr); + projectedValueInit = buildPropertyWrapperInitCall(var, backingVar->getType(), + placeholder, PropertyWrapperInitKind::ProjectedValue); + TypeChecker::typeCheckExpression(projectedValueInit, dc); + + // Check initializer effects. + auto *initContext = new (ctx) PropertyWrapperInitializer( + dc, param, PropertyWrapperInitializer::Kind::ProjectedValue); + TypeChecker::checkInitializerEffects(initContext, projectedValueInit); + } } // If no initial wrapped value was provided via '=' and either: @@ -2790,22 +2856,85 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator, // value. if (!wrappedValue && (!var->allAttachedPropertyWrappersHaveWrappedValueInit() || initializer)) { - return PropertyWrapperBackingPropertyInfo( - backingVar, storageVar, nullptr, nullptr); + return PropertyWrapperBackingPropertyInfo(backingVar, projectionVar, nullptr, + projectedValueInit); } // Form the initialization of the backing property from a value of the // original property's type. - if (!initializer) { - initializer = PropertyWrapperValuePlaceholderExpr::create( + Expr *wrappedValueInit = initializer; + if (!wrappedValueInit) { + wrappedValueInit = PropertyWrapperValuePlaceholderExpr::create( ctx, var->getSourceRange(), var->getType(), /*wrappedValue=*/nullptr); - typeCheckSynthesizedWrapperInitializer( - pbd, backingVar, parentPBD, initializer); - wrappedValue = findWrappedValuePlaceholder(initializer); + + if (auto *param = dyn_cast(var)) { + wrappedValueInit = buildPropertyWrapperInitCall(var, backingVar->getType(), wrappedValueInit, + PropertyWrapperInitKind::WrappedValue); + TypeChecker::typeCheckExpression(wrappedValueInit, dc); + + // Check initializer effects. + auto *initContext = new (ctx) PropertyWrapperInitializer( + dc, param, PropertyWrapperInitializer::Kind::WrappedValue); + TypeChecker::checkInitializerEffects(initContext, wrappedValueInit); + } else { + typeCheckSynthesizedWrapperInitializer( + pbd, backingVar, var->getParentPatternBinding(), wrappedValueInit); + } + } + + return PropertyWrapperBackingPropertyInfo(backingVar, projectionVar, + wrappedValueInit, + projectedValueInit); +} + +VarDecl * +PropertyWrapperWrappedValueVarRequest::evaluate(Evaluator &evaluator, + VarDecl *var) const { + auto wrapperInfo = var->getPropertyWrapperBackingPropertyInfo(); + if (!wrapperInfo || !isa(var)) + return nullptr; + + auto dc = var->getDeclContext(); + auto &ctx = var->getASTContext(); + + SmallString<64> nameBuf; + if (var->getName().hasDollarPrefix()) { + nameBuf = var->getName().str().drop_front(); + } else { + nameBuf = var->getName().str(); + } + Identifier name = ctx.getIdentifier(nameBuf); + + VarDecl *localVar = new (ctx) VarDecl(/*IsStatic=*/false, + VarDecl::Introducer::Var, + var->getLoc(), name, dc); + if (!var->hasImplicitPropertyWrapper()) + localVar->setInterfaceType(var->getInterfaceType()); + localVar->setImplicit(); + localVar->getAttrs() = var->getAttrs(); + localVar->overwriteAccess(var->getFormalAccess()); + + if (var->hasImplicitPropertyWrapper()) { + // FIXME: This can have a setter, but we need a resolved wrapper type + // to figure it out. + localVar->setImplInfo(StorageImplInfo::getImmutableComputed()); + } else { + auto mutability = *var->getPropertyWrapperMutability(); + if (mutability.Getter == PropertyWrapperMutability::Mutating) { + ctx.Diags.diagnose(var->getLoc(), diag::property_wrapper_param_mutating); + return nullptr; + } + + if (mutability.Setter == PropertyWrapperMutability::Nonmutating) { + localVar->setImplInfo(StorageImplInfo::getMutableComputed()); + } else { + localVar->setImplInfo(StorageImplInfo::getImmutableComputed()); + } } - return PropertyWrapperBackingPropertyInfo(backingVar, storageVar, - initializer, wrappedValue); + evaluator.cacheOutput(PropertyWrapperBackingPropertyInfoRequest{localVar}, + std::move(wrapperInfo)); + return localVar; } /// Given a storage declaration in a protocol, set it up with the right diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 1d1e34a41158d..d979ce4b76113 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -26,6 +26,7 @@ #include "swift/AST/KnownProtocols.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/NameLookup.h" +#include "swift/AST/PropertyWrappers.h" #include "swift/AST/TypeRefinementContext.h" #include "swift/Parse/Lexer.h" #include "swift/Basic/OptionSet.h" @@ -689,8 +690,7 @@ bool typeCheckExprPattern(ExprPattern *EP, DeclContext *DC, Type type); /// Coerce the specified parameter list of a ClosureExpr to the specified /// contextual type. -void coerceParameterListToType(ParameterList *P, ClosureExpr *CE, - AnyFunctionType *FN); +void coerceParameterListToType(ParameterList *P, AnyFunctionType *FN); /// Type-check an initialized variable pattern declaration. bool typeCheckBinding(Pattern *&P, Expr *&Init, DeclContext *DC, @@ -1245,15 +1245,22 @@ bool isValidKeyPathDynamicMemberLookup(SubscriptDecl *decl, /// \param limit How many levels of unwrapping to perform, where 0 means to return the /// \c backingStorageType directly and the maximum is the number of attached property wrappers /// (which will produce the original property type). If not specified, defaults to the maximum. -Type computeWrappedValueType(VarDecl *var, Type backingStorageType, +Type computeWrappedValueType(const VarDecl *var, Type backingStorageType, Optional limit = None); -/// Build a call to the init(wrappedValue:) initializers of the property -/// wrappers, filling in the given \c value as the original value. Optionally -/// pass a callback that will get invoked with the innermost init(wrappedValue:) -/// call. -Expr *buildPropertyWrapperWrappedValueCall( - VarDecl *var, Type backingStorageType, Expr *value, bool ignoreAttributeArgs, +/// Compute the projected value type for the given property that has attached +/// property wrappers when the backing storage is known to have the given type. +Type computeProjectedValueType(const VarDecl *var, Type backingStorageType); + +/// Build a call to the init(wrappedValue:) or init(projectedValue:) +/// initializer of the property wrapper, filling in the given \c value +/// as the wrapped or projected value argument. +/// +/// Optionally pass a callback that will get invoked with the innermost init +/// apply expression. +Expr *buildPropertyWrapperInitCall( + const VarDecl *var, Type backingStorageType, Expr *value, + PropertyWrapperInitKind initKind, llvm::function_ref callback = [](ApplyExpr *) {}); /// Whether an overriding declaration requires the 'override' keyword. diff --git a/lib/TBDGen/TBDGen.cpp b/lib/TBDGen/TBDGen.cpp index d65a68f6617c6..57fe55f5cb4c0 100644 --- a/lib/TBDGen/TBDGen.cpp +++ b/lib/TBDGen/TBDGen.cpp @@ -847,7 +847,7 @@ void TBDGenVisitor::visitVarDecl(VarDecl *VD) { // Wrapped non-static member properties may have a backing initializer. if (auto wrapperInfo = VD->getPropertyWrapperBackingPropertyInfo()) { - if (wrapperInfo.initializeFromOriginal && !VD->isStatic()) { + if (wrapperInfo.hasInitFromWrappedValue() && !VD->isStatic()) { addSymbol( SILDeclRef(VD, SILDeclRef::Kind::PropertyWrapperBackingInitializer)); } diff --git a/test/Parse/dollar_identifier.swift b/test/Parse/dollar_identifier.swift index 287ca04f951bc..fbb14e21b8fb2 100644 --- a/test/Parse/dollar_identifier.swift +++ b/test/Parse/dollar_identifier.swift @@ -77,7 +77,7 @@ func $declareWithDollar() { // expected-error{{cannot declare entity named '$dec $b c: Int) { } // expected-error{{cannot declare entity named '$b'}} let _: (Int) -> Int = { [$capture = 0] // expected-error{{cannot declare entity named '$capture'}} - $a in // expected-error{{cannot declare entity named '$a'}} + $a in // expected-error{{inferred projection type 'Int' is not a property wrapper}} $capture } let ($a: _, _) = (0, 0) // expected-error{{cannot declare entity named '$a'}} diff --git a/test/SILGen/property_wrapper_parameter.swift b/test/SILGen/property_wrapper_parameter.swift new file mode 100644 index 0000000000000..5045ca7882997 --- /dev/null +++ b/test/SILGen/property_wrapper_parameter.swift @@ -0,0 +1,231 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s + +public struct Projection { + public var wrappedValue: T +} + +@propertyWrapper +public struct Wrapper { + public var wrappedValue: T + + // CHECK-LABEL: sil [ossa] @$s26property_wrapper_parameter7WrapperV12wrappedValueACyxGx_tcfC : $@convention(method) (@in T, @thin Wrapper.Type) -> @out Wrapper + public init(wrappedValue: T) { + self.wrappedValue = wrappedValue + } + + public var projectedValue: Projection { + Projection(wrappedValue: wrappedValue) + } + + // CHECK-LABEL: sil [ossa] @$s26property_wrapper_parameter7WrapperV14projectedValueACyxGAA10ProjectionVyxG_tcfC : $@convention(method) (@in Projection, @thin Wrapper.Type) -> @out Wrapper + public init(projectedValue: Projection) { + self.wrappedValue = projectedValue.wrappedValue + } +} + +// CHECK-LABEL: sil [ossa] @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tF : $@convention(thin) (Wrapper) -> () +public func testSimpleWrapperParameter(@Wrapper value: Int) { + _ = value + _ = _value + _ = $value + + // property wrapper backing initializer of value #1 in testSimpleWrapperParameter(value:) + // CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tFACL_SivpfP : $@convention(thin) (Int) -> Wrapper + + // property wrapper init from projected value of value #1 in testSimpleWrapperParameter(value:) + // CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tFACL_SivpfW : $@convention(thin) (Projection) -> Wrapper + + // getter of $value #1 in testSimpleWrapperParameter(value:) + // CHECK: sil private [ossa] @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tF6$valueL_AA10ProjectionVySiGvg : $@convention(thin) (Wrapper) -> Projection + + // getter of value #1 in testSimpleWrapperParameter(value:) + // CHECK: sil private [ossa] @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tFACL_Sivg : $@convention(thin) (Wrapper) -> Int +} + +// CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_parameter28simpleWrapperParameterCaller10projectionyAA10ProjectionVySiG_tF : $@convention(thin) (Projection) -> () +func simpleWrapperParameterCaller(projection: Projection) { + testSimpleWrapperParameter(value: projection.wrappedValue) + // CHECK: function_ref @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tFACL_SivpfP : $@convention(thin) (Int) -> Wrapper + + testSimpleWrapperParameter($value: projection) + // CHECK: function_ref @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tFACL_SivpfW : $@convention(thin) (Projection) -> Wrapper +} + +// CHECK-LABEL: sil [ossa] @$s26property_wrapper_parameter18testGenericWrapper5valueyAA0F0VyxG_tlF : $@convention(thin) (@in_guaranteed Wrapper) -> () +public func testGenericWrapper(@Wrapper value: T) { + + // property wrapper backing initializer of value #1 in testGenericWrapper(value:) + // CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter18testGenericWrapper5valueyAA0F0VyxG_tlFACL_xvpfP : $@convention(thin) (@in T) -> @out Wrapper + + // property wrapper init from projected value of value #1 in testGenericWrapper(value:) + // CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter18testGenericWrapper5valueyAA0F0VyxG_tlFACL_xvpfW : $@convention(thin) (@in Projection) -> @out Wrapper +} + +// CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_parameter20genericWrapperCaller10projectionyAA10ProjectionVySiG_tF : $@convention(thin) (Projection) -> () +func genericWrapperCaller(projection: Projection) { + testGenericWrapper(value: projection.wrappedValue) + // CHECK: function_ref @$s26property_wrapper_parameter18testGenericWrapper5valueyAA0F0VyxG_tlFACL_xvpfP : $@convention(thin) <τ_0_0> (@in τ_0_0) -> @out Wrapper<τ_0_0> + + testGenericWrapper($value: projection) + // CHECK: function_ref @$s26property_wrapper_parameter18testGenericWrapper5valueyAA0F0VyxG_tlFACL_xvpfW : $@convention(thin) <τ_0_0> (@in Projection<τ_0_0>) -> @out Wrapper<τ_0_0> +} + +// CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_parameter33testSimpleClosureWrapperParameteryyF : $@convention(thin) () -> () +func testSimpleClosureWrapperParameter() { + let closure: (Int) -> Void = { (@Wrapper value: Int) in + _ = value + _ = _value + _ = $value + } + + closure(10) + + // implicit closure #1 in testSimpleClosureWrapperParameter() + // CHECK: sil private [ossa] @$s26property_wrapper_parameter33testSimpleClosureWrapperParameteryyFySicfu_ : $@convention(thin) (Int) -> () + + // closure #1 in implicit closure #1 in testSimpleClosureWrapperParameter() + // CHECK: sil private [ossa] @$s26property_wrapper_parameter33testSimpleClosureWrapperParameteryyFySicfu_yAA0G0VySiGcfU_ : $@convention(thin) (Wrapper) -> () + + // property wrapper backing initializer of value #1 in closure #1 in implicit closure #1 in testSimpleClosureWrapperParameter() + // CHECK: sil private [ossa] @$s26property_wrapper_parameter33testSimpleClosureWrapperParameteryyFySicfu_yAA0G0VySiGcfU_5valueL_SivpfP : $@convention(thin) (Int) -> Wrapper + + // getter of $value #1 in closure #1 in implicit closure #1 in testSimpleClosureWrapperParameter() + // CHECK: sil private [ossa] @$s26property_wrapper_parameter33testSimpleClosureWrapperParameteryyFySicfu_yAA0G0VySiGcfU_6$valueL_AA10ProjectionVySiGvg : $@convention(thin) (Wrapper) -> Projection + + // getter of value #1 in closure #1 in implicit closure #1 in testSimpleClosureWrapperParameter() + // CHECK: sil private [ossa] @$s26property_wrapper_parameter33testSimpleClosureWrapperParameteryyFySicfu_yAA0G0VySiGcfU_5valueL_Sivg : $@convention(thin) (Wrapper) -> Int +} + +@propertyWrapper +struct NonMutatingSetterWrapper { + private var value: Value + + var wrappedValue: Value { + get { value } + nonmutating set { } + } + + init(wrappedValue: Value) { + self.value = wrappedValue + } +} + +@propertyWrapper +class ClassWrapper { + var wrappedValue: Value + + init(wrappedValue: Value) { + self.wrappedValue = wrappedValue + } +} + +// CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_parameter21testNonMutatingSetter6value16value2yAA0efG7WrapperVySSG_AA05ClassJ0CySiGtF : $@convention(thin) (@guaranteed NonMutatingSetterWrapper, @guaranteed ClassWrapper) -> () +func testNonMutatingSetter(@NonMutatingSetterWrapper value1: String, @ClassWrapper value2: Int) { + _ = value1 + value1 = "hello!" + + // property wrapper backing initializer of value1 #1 in testNonMutatingSetter(value1:value2:) + // CHECK: sil private [ossa] @$s26property_wrapper_parameter21testNonMutatingSetter6value16value2yAA0efG7WrapperVySSG_AA05ClassJ0CySiGtFACL_SSvpfP : $@convention(thin) (@owned String) -> @owned NonMutatingSetterWrapper + + // property wrapper backing initializer of value2 #1 in testNonMutatingSetter(value1:value2:) + // CHECK: sil private [ossa] @$s26property_wrapper_parameter21testNonMutatingSetter6value16value2yAA0efG7WrapperVySSG_AA05ClassJ0CySiGtFADL_SivpfP : $@convention(thin) (Int) -> @owned ClassWrapper + + // getter of value1 #1 in testNonMutatingSetter(value1:value2:) + // CHECK: sil private [ossa] @$s26property_wrapper_parameter21testNonMutatingSetter6value16value2yAA0efG7WrapperVySSG_AA05ClassJ0CySiGtFACL_SSvg : $@convention(thin) (@guaranteed NonMutatingSetterWrapper) -> @owned String + + // setter of value1 #1 in testNonMutatingSetter(value1:value2:) + // CHECK: sil private [ossa] @$s26property_wrapper_parameter21testNonMutatingSetter6value16value2yAA0efG7WrapperVySSG_AA05ClassJ0CySiGtFACL_SSvs : $@convention(thin) (@owned String, @guaranteed NonMutatingSetterWrapper) -> () + + _ = value2 + value2 = 10 + + // getter of value2 #1 in testNonMutatingSetter(value1:value2:) + // CHECK: sil private [ossa] @$s26property_wrapper_parameter21testNonMutatingSetter6value16value2yAA0efG7WrapperVySSG_AA05ClassJ0CySiGtFADL_Sivg : $@convention(thin) (@guaranteed ClassWrapper) -> Int + + // setter of value2 #1 in testNonMutatingSetter(value1:value2:) + // CHECK: sil private [ossa] @$s26property_wrapper_parameter21testNonMutatingSetter6value16value2yAA0efG7WrapperVySSG_AA05ClassJ0CySiGtFADL_Sivs : $@convention(thin) (Int, @guaranteed ClassWrapper) -> () +} + +@propertyWrapper +struct ProjectionWrapper { + var wrappedValue: Value + + var projectedValue: ProjectionWrapper { self } + + init(wrappedValue: Value) { self.wrappedValue = wrappedValue } + + init(projectedValue: ProjectionWrapper) { + self.wrappedValue = projectedValue.wrappedValue + } +} + +// CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_parameter27testImplicitPropertyWrapper10projectionyAA010ProjectionG0VySiG_tF : $@convention(thin) (ProjectionWrapper) -> () +func testImplicitPropertyWrapper(projection: ProjectionWrapper) { + let multiStatement: (ProjectionWrapper) -> Void = { ($value: ProjectionWrapper) in + _ = value + _ = _value + _ = $value + } + + multiStatement(projection) + + // implicit closure #1 in testImplicitPropertyWrapper(projection:) + // CHECK: sil private [ossa] @$s26property_wrapper_parameter27testImplicitPropertyWrapper10projectionyAA010ProjectionG0VySiG_tFyAFcfu_ : $@convention(thin) (ProjectionWrapper) -> () + + // closure #1 in implicit closure #1 in testImplicitPropertyWrapper(projection:) + // CHECK: sil private [ossa] @$s26property_wrapper_parameter27testImplicitPropertyWrapper10projectionyAA010ProjectionG0VySiG_tFyAFcfu_yAFcfU_ : $@convention(thin) (ProjectionWrapper) -> () + + // property wrapper init from projected value of $value #1 in closure #1 in implicit closure #1 in testImplicitPropertyWrapper(projection:) + // CHECK: sil private [ossa] @$s26property_wrapper_parameter27testImplicitPropertyWrapper10projectionyAA010ProjectionG0VySiG_tFyAFcfu_yAFcfU_6$valueL_AFvpfW : $@convention(thin) (ProjectionWrapper) -> ProjectionWrapper + + // getter of $value #1 in closure #1 in implicit closure #1 in testImplicitPropertyWrapper(projection:) + // CHECK: sil private [ossa] @$s26property_wrapper_parameter27testImplicitPropertyWrapper10projectionyAA010ProjectionG0VySiG_tFyAFcfu_yAFcfU_6$valueL_AFvg : $@convention(thin) (ProjectionWrapper) -> ProjectionWrapper + + // getter of value #1 in closure #1 in implicit closure #1 in testImplicitPropertyWrapper(projection:) + // CHECK: sil private [ossa] @$s26property_wrapper_parameter27testImplicitPropertyWrapper10projectionyAA010ProjectionG0VySiG_tFyAFcfu_yAFcfU_5valueL_Sivg : $@convention(thin) () -> Int + + let _: (ProjectionWrapper) -> (Int, ProjectionWrapper) = { ($value: ProjectionWrapper) in + (value, $value) + } + + // implicit closure #2 in testImplicitPropertyWrapper(projection:) + // CHECK: sil private [ossa] @$s26property_wrapper_parameter27testImplicitPropertyWrapper10projectionyAA010ProjectionG0VySiG_tFSi_AFtAFcfu0_ : $@convention(thin) (ProjectionWrapper) -> (Int, ProjectionWrapper) + + // closure #2 in implicit closure #2 in testImplicitPropertyWrapper(projection:) + // CHECK: sil private [ossa] @$s26property_wrapper_parameter27testImplicitPropertyWrapper10projectionyAA010ProjectionG0VySiG_tFSi_AFtAFcfu0_Si_AFtAFcfU0_ : $@convention(thin) (ProjectionWrapper) -> (Int, ProjectionWrapper) + + // property wrapper init from projected value of $value #1 in closure #2 in implicit closure #2 in testImplicitPropertyWrapper(projection:) + // CHECK: sil private [ossa] @$s26property_wrapper_parameter27testImplicitPropertyWrapper10projectionyAA010ProjectionG0VySiG_tFSi_AFtAFcfu0_Si_AFtAFcfU0_6$valueL_AFvpfW : $@convention(thin) (ProjectionWrapper) -> ProjectionWrapper + + // getter of $value #1 in closure #2 in implicit closure #2 in testImplicitPropertyWrapper(projection:) + // CHECK: sil private [ossa] @$s26property_wrapper_parameter27testImplicitPropertyWrapper10projectionyAA010ProjectionG0VySiG_tFSi_AFtAFcfu0_Si_AFtAFcfU0_6$valueL_AFvg : $@convention(thin) (ProjectionWrapper) -> ProjectionWrapper + + // getter of value #1 in closure #2 in implicit closure #2 in testImplicitPropertyWrapper(projection:) + // CHECK: sil private [ossa] @$s26property_wrapper_parameter27testImplicitPropertyWrapper10projectionyAA010ProjectionG0VySiG_tFSi_AFtAFcfu0_Si_AFtAFcfU0_5valueL_Sivg : $@convention(thin) () -> Int +} + +@propertyWrapper +public struct PublicWrapper { + public var wrappedValue: T + + public init(wrappedValue: T) { + self.wrappedValue = wrappedValue + } + + public var projectedValue: PublicWrapper { + return self + } + + public init(projectedValue: PublicWrapper) { + self.wrappedValue = projectedValue.wrappedValue + } +} + +// CHECK-LABEL: sil [ossa] @$s26property_wrapper_parameter10publicFunc5valueyAA13PublicWrapperVySSG_tF : $@convention(thin) (@guaranteed PublicWrapper) -> () +public func publicFunc(@PublicWrapper value: String) { + // property wrapper backing initializer of value #1 in publicFunc(value:) + // CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter10publicFunc5valueyAA13PublicWrapperVySSG_tFACL_SSvpfP : $@convention(thin) (@owned String) -> @owned PublicWrapper + + // property wrapper init from projected value of value #1 in publicFunc(value:) + // CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter10publicFunc5valueyAA13PublicWrapperVySSG_tFACL_SSvpfW : $@convention(thin) (@owned PublicWrapper) -> @owned PublicWrapper +} diff --git a/test/Sema/property_wrapper_parameter.swift b/test/Sema/property_wrapper_parameter.swift new file mode 100644 index 0000000000000..3ba6b13d4d3fc --- /dev/null +++ b/test/Sema/property_wrapper_parameter.swift @@ -0,0 +1,110 @@ +// RUN: %target-typecheck-verify-swift + +struct Projection { + var value: T +} + +@propertyWrapper +struct Wrapper { + var wrappedValue: T + + init(wrappedValue: T) { + self.wrappedValue = wrappedValue + } + + var projectedValue: Projection { + Projection(value: wrappedValue) + } + + init(projectedValue: Projection) { + self.wrappedValue = projectedValue.value + } +} + +func globalFunc(@Wrapper arg: Int) { + let _: Int = arg + let _: Projection = $arg + let _: Wrapper = _arg +} + +func testGloablFunc(value: Int, projection: Projection) { + globalFunc(arg: value) + globalFunc($arg: projection) + + let _: (Int) -> Void = globalFunc + let _: (Int) -> Void = globalFunc(arg:) + let _: (Projection) -> Void = globalFunc($arg:) +} + + +struct S { + func method(@Wrapper arg: Value) { + let _: Value = arg + let _: Projection = $arg + let _: Wrapper = _arg + } + + static func staticMethod(@Wrapper arg: Value) { + let _: Value = arg + let _: Projection = $arg + let _: Wrapper = _arg + } +} + +func testMethods(instance: S, Metatype: S.Type, + @Wrapper value: String) { + Metatype.staticMethod(arg: value) + Metatype.staticMethod($arg: $value) + + instance.method(arg: value) + instance.method($arg: $value) + + let _: (String) -> Void = Metatype.staticMethod + let _: (String) -> Void = Metatype.staticMethod(arg:) + let _: (Projection) -> Void = Metatype.staticMethod($arg:) + + let _: (String) -> Void = instance.method + let _: (String) -> Void = instance.method(arg:) + let _: (Projection) -> Void = instance.method($arg:) + + let _: (String) -> Void = instance.method + let _: (String) -> Void = instance.method(arg:) + let _: (Projection) -> Void = instance.method($arg:) + + let _: (S) -> (String) -> Void = Metatype.method + let _: (S) -> (String) -> Void = Metatype.method(arg:) + let _: (S) -> (Projection) -> Void = Metatype.method($arg:) +} + +func testClosures() { + typealias PropertyWrapperTuple = (Wrapper, Int, Projection) + + let _: (Int) -> PropertyWrapperTuple = { (@Wrapper value) in + (_value, value, $value) + } + + let _: (Projection) -> PropertyWrapperTuple = { (@Wrapper $value) in + (_value, value, $value) + } +} + +@propertyWrapper +struct ProjectionWrapper { + var wrappedValue: Value + + var projectedValue: ProjectionWrapper { self } + + init(wrappedValue: Value) { self.wrappedValue = wrappedValue } + + init(projectedValue: ProjectionWrapper) { + self.wrappedValue = projectedValue.wrappedValue + } +} + +func testImplicitPropertyWrapper() { + typealias PropertyWrapperTuple = (ProjectionWrapper, Int, ProjectionWrapper) + + let _: (ProjectionWrapper) -> PropertyWrapperTuple = { $value in + (_value, value, $value) + } +} diff --git a/test/Sema/property_wrapper_parameter_invalid.swift b/test/Sema/property_wrapper_parameter_invalid.swift new file mode 100644 index 0000000000000..6c84263b5337c --- /dev/null +++ b/test/Sema/property_wrapper_parameter_invalid.swift @@ -0,0 +1,90 @@ +// RUN: %target-typecheck-verify-swift + +@propertyWrapper +struct NonMutatingWrapper { + var wrappedValue: T { + get { fatalError() } + nonmutating set { fatalError() } + } + + init(wrappedValue: T) { fatalError() } +} + +@propertyWrapper +struct MutatingWrapper { + var wrappedValue: T { + mutating get { fatalError() } + } + + init(wrappedValue: T) { fatalError() } +} + +// expected-error@+1 {{property wrapper applied to parameter must have a nonmutating 'wrappedValue' getter}} +func testMutatingGetter(@MutatingWrapper value: Int) {} + +// expected-error@+1 {{property wrapper applied to parameter must have a nonmutating 'wrappedValue' getter}} +func testComposedMutating(@MutatingWrapper @NonMutatingWrapper value: Int) {} + +// okay +func testComposedNonMutating(@NonMutatingWrapper @MutatingWrapper value: Int) {} + +@propertyWrapper +struct NoProjection { + var wrappedValue: T +} + +func takesNoProjectionWrapper(@NoProjection value: String) {} + +func testNoProjection(message: String) { + takesNoProjectionWrapper(value: message) // okay + + // expected-error@+1 {{cannot use property wrapper projection parameter; wrapper 'NoProjection' does not have a 'projectedValue'}} + takesNoProjectionWrapper($value: message) +} + +struct Projection { + var value: T +} + +@propertyWrapper +struct Wrapper { + init(wrappedValue: T) { + self.wrappedValue = wrappedValue + } + + init(projectedValue: Projection) { + self.wrappedValue = projectedValue.value + } + + var wrappedValue: T + var projectedValue: Projection { Projection(value: wrappedValue) } +} + +func hasWrapperAttributeArg(@Wrapper() value: T) {} + +func testWrapperAttributeArg(projection: Projection) { + hasWrapperAttributeArg(value: projection.value) + + // expected-error@+1 {{cannot use property wrapper projection argument; pass wrapped value type 'Int' instead}} + hasWrapperAttributeArg($value: projection) +} + +struct S { + // expected-error@+1 {{property wrapper attribute on parameter not yet supported on subscript}} + subscript(@Wrapper position: Int) -> Int { 0 } +} + +func testInvalidArgLabel() { + func noWrappers(argLabel: Int) {} + + // expected-error@+1 {{cannot use property wrapper projection argument; parameter does not have an attached property wrapper}} + let ref = noWrappers($argLabel:) + + // expected-error@+1 {{cannot use property wrapper projection argument; parameter does not have an attached property wrapper}} + noWrappers($argLabel: 10) +} + +protocol P { + // expected-error@+1 {{parameter 'arg' declared inside a protocol cannot have a wrapper}} + func requirement(@Wrapper arg: Int) +}