From 376a859417e0410bc1cdc1b7316118ab7f107b12 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 27 Aug 2020 17:31:54 -0700 Subject: [PATCH 01/38] [Property Wrappers] Handle property wrappers on parameters in PropertyWrapperBackingPropertyInfoRequest. --- lib/Sema/TypeCheckPropertyWrapper.cpp | 13 ++++---- lib/Sema/TypeCheckStorage.cpp | 47 ++++++++++++++++++++------- 2 files changed, 43 insertions(+), 17 deletions(-) diff --git a/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index 36db5a9203da4..dfe7702d9f94f 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -421,14 +421,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; } @@ -530,14 +530,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->getDeclContext()->getAsDecl()) 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)) diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 5e07b72fa7cc9..311b1a25b059d 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -2444,9 +2444,10 @@ static VarDecl *synthesizePropertyWrapperProjectionVar( 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 +2455,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); @@ -2485,7 +2487,7 @@ static VarDecl *synthesizePropertyWrapperProjectionVar( var->getAttrs().add( new (ctx) ProjectedValuePropertyAttr(name, SourceLoc(), SourceRange(), - /*Implicit=*/true)); + /*Implicit=*/true)); return property; } @@ -2684,11 +2686,6 @@ 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()) - return PropertyWrapperBackingPropertyInfo(); - auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(0); if (!wrapperInfo) return PropertyWrapperBackingPropertyInfo(); @@ -2700,8 +2697,36 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator, nameBuf += var->getName().str(); Identifier name = ctx.getIdentifier(nameBuf); - // Determine the type of the storage. auto dc = var->getDeclContext(); + if (auto *param = dyn_cast(var)) { + auto *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 (dc->getAsDecl()) { + wrapperType = var->getPropertyWrapperBackingPropertyType(); + if (!wrapperType || wrapperType->hasError()) + return PropertyWrapperBackingPropertyInfo(); + + backingVar->setInterfaceType(wrapperType); + } + + VarDecl *projectionVar = nullptr; + if (wrapperInfo.projectedValueVar) { + projectionVar = synthesizePropertyWrapperProjectionVar(ctx, var, wrapperType, + wrapperInfo.projectedValueVar); + } + + return PropertyWrapperBackingPropertyInfo(backingVar, projectionVar, nullptr, nullptr); + } + + // Determine the type of the storage. + auto wrapperType = var->getPropertyWrapperBackingPropertyType(); + if (!wrapperType || wrapperType->hasError()) + return PropertyWrapperBackingPropertyInfo(); Type storageInterfaceType = wrapperType; Type storageType = dc->mapTypeIntoContext(storageInterfaceType); From 8325a52b4153a8fe86b3ffd562fca5f21c52f7ae Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 26 Aug 2020 16:38:06 -0700 Subject: [PATCH 02/38] [ConstraintSystem] Teach the constraint system about property wrapper parameters. --- include/swift/AST/Types.h | 6 ++ include/swift/Sema/ConstraintSystem.h | 16 ++++ lib/AST/Decl.cpp | 3 + lib/AST/Type.cpp | 10 +++ lib/Sema/CSApply.cpp | 8 ++ lib/Sema/CSGen.cpp | 113 ++++++++++++++------------ lib/Sema/CSSimplify.cpp | 55 ++++++++++++- lib/Sema/CSSolver.cpp | 14 +++- lib/Sema/TypeCheckPattern.cpp | 15 ++++ lib/Sema/TypeCheckPropertyWrapper.cpp | 4 +- lib/Sema/TypeChecker.h | 4 +- 11 files changed, 190 insertions(+), 58 deletions(-) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 956ead46c4775..fddfd61b2fbd8 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -80,6 +80,7 @@ class EnumDecl; class EnumElementDecl; class SILFunctionType; class StructDecl; +class ParamDecl; class ProtocolDecl; class TypeVariableType; class ValueDecl; @@ -3280,6 +3281,7 @@ END_CAN_TYPE_WRAPPER(FunctionType, AnyFunctionType) struct ParameterListInfo { SmallBitVector defaultArguments; SmallBitVector acceptsUnlabeledTrailingClosures; + SmallVector 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/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 125eba8bab6b7..709117559adb9 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; @@ -2230,6 +2233,9 @@ class ConstraintSystem { std::vector> resultBuilderTransformed; + /// A map from argument expressions to their applied property wrapper expressions. + llvm::SmallMapVector appliedPropertyWrappers; + /// Cache of the effects any closures visited. llvm::SmallDenseMap closureEffectsCache; @@ -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,13 @@ class ConstraintSystem { ConstraintKind bodyResultConstraintKind, ConstraintLocatorBuilder locator); + /// Matches a wrapped value argument type to its backing wrapper type by applying + /// the property wrapper and generating constraints for the backing initializer + /// expression. + TypeMatchResult matchPropertyWrapperArgument( + Type wrapperType, Type wrappedValueArgumentType, const ParamDecl *param, + ConstraintKind matchKind, ConstraintLocatorBuilder locator); + Optional determineBestBindings(); /// Get bindings for the given type variable based on current diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 198d93574438c..c7c9167bc4afe 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -6245,6 +6245,9 @@ AnyFunctionType::Param ParamDecl::toFunctionParam(Type type) const { if (isVariadic()) type = ParamDecl::getVarargBaseTy(type); + if (auto wrapperType = getPropertyWrapperBackingPropertyType()) + type = wrapperType; + auto label = getArgumentName(); auto flags = ParameterTypeFlags::fromParameterType( type, isVariadic(), isAutoClosure(), isNonEphemeral(), 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/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index d3c5bcb27eabf..1254abf42e3ae 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7616,6 +7616,14 @@ namespace { } std::pair walkToExprPre(Expr *expr) override { + // If this expression has an applied property wrapper, get the backing + // initializer expression. + if (Rewriter.solution.appliedPropertyWrappers.count(expr)) { + auto *init = Rewriter.solution.appliedPropertyWrappers[expr]; + Rewriter.solution.appliedPropertyWrappers.erase(expr); + expr = init; + } + // For closures, update the parameter types and check the body. if (auto closure = dyn_cast(expr)) { rewriteFunction(closure); diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 14628286925d5..2ad86c5c986cd 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -777,6 +777,55 @@ namespace { namespace { + /// Generate constraints to produce the wrapped value type given the property + /// that has an attached property wrapper. + /// + /// \param initializerType The type of the adjusted initializer, which + /// initializes the underlying storage variable. + /// \param wrappedVar The property that has a property wrapper. + /// \returns the type of the property. + bool generateWrappedPropertyTypeConstraints( + ConstraintSystem &cs, Type initializerType, VarDecl *wrappedVar, Type propertyType) { + auto dc = wrappedVar->getInnermostDeclContext(); + + Type wrapperType = LValueType::get(initializerType); + Type wrappedValueType; + + auto wrapperAttributes = wrappedVar->getAttachedPropertyWrappers(); + for (unsigned i : indices(wrapperAttributes)) { + // FIXME: We should somehow pass an OpenUnboundGenericTypeFn to + // AttachedPropertyWrapperTypeRequest::evaluate to open up unbound + // generics on the fly. + Type rawWrapperType = wrappedVar->getAttachedPropertyWrapperType(i); + auto wrapperInfo = wrappedVar->getAttachedPropertyWrapperTypeInfo(i); + if (rawWrapperType->hasError() || !wrapperInfo) + return true; + + // The former wrappedValue type must be equal to the current wrapper type + if (wrappedValueType) { + auto *typeRepr = wrapperAttributes[i]->getTypeRepr(); + auto *locator = + cs.getConstraintLocator(typeRepr, LocatorPathElt::ContextualType()); + wrapperType = cs.replaceInferableTypesWithTypeVars(rawWrapperType, + locator); + cs.addConstraint(ConstraintKind::Equal, wrapperType, wrappedValueType, + locator); + cs.setContextualType(typeRepr, TypeLoc::withoutLoc(wrappedValueType), + CTP_ComposedPropertyWrapper); + } + + wrappedValueType = wrapperType->getTypeOfMember( + dc->getParentModule(), wrapperInfo.valueVar); + } + + // The property type must be equal to the wrapped value type + cs.addConstraint(ConstraintKind::Equal, propertyType, wrappedValueType, + cs.getConstraintLocator(wrappedVar, LocatorPathElt::ContextualType())); + cs.setContextualType(wrappedVar, TypeLoc::withoutLoc(wrappedValueType), + CTP_WrappedProperty); + return false; + } + class ConstraintGenerator : public ExprVisitor { ConstraintSystem &CS; DeclContext *CurDC; @@ -1975,7 +2024,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)); @@ -2004,6 +2053,18 @@ namespace { TVO_CanBindToInOut | TVO_CanBindToNoEscape | TVO_CanBindToHole); } + if (param->hasAttachedPropertyWrapper()) { + auto *wrapperAttr = param->getAttachedPropertyWrappers().front(); + auto wrapperType = param->getAttachedPropertyWrapperType(0); + auto backingType = CS.openUnboundGenericTypes( + wrapperType, CS.getConstraintLocator(wrapperAttr->getTypeRepr())); + if (generateWrappedPropertyTypeConstraints(CS, backingType, param, externalType)) + return nullptr; + + // The external parameter type is the backing property wrapper type + externalType = backingType; + } + closureParams.push_back(param->toFunctionParam(externalType)); } } @@ -3548,56 +3609,6 @@ static Expr *generateConstraintsFor(ConstraintSystem &cs, Expr *expr, return result; } -/// Generate constraints to produce the wrapped value type given the property -/// that has an attached property wrapper. -/// -/// \param initializerType The type of the adjusted initializer, which -/// initializes the underlying storage variable. -/// \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) { - auto dc = wrappedVar->getInnermostDeclContext(); - - Type wrapperType = LValueType::get(initializerType); - Type wrappedValueType; - - auto wrapperAttributes = wrappedVar->getAttachedPropertyWrappers(); - for (unsigned i : indices(wrapperAttributes)) { - // FIXME: We should somehow pass an OpenUnboundGenericTypeFn to - // AttachedPropertyWrapperTypeRequest::evaluate to open up unbound - // generics on the fly. - Type rawWrapperType = wrappedVar->getAttachedPropertyWrapperType(i); - auto wrapperInfo = wrappedVar->getAttachedPropertyWrapperTypeInfo(i); - if (rawWrapperType->hasError() || !wrapperInfo) - return true; - - // The former wrappedValue type must be equal to the current wrapper type - if (wrappedValueType) { - auto *typeRepr = wrapperAttributes[i]->getTypeRepr(); - auto *locator = - cs.getConstraintLocator(typeRepr, LocatorPathElt::ContextualType()); - wrapperType = cs.replaceInferableTypesWithTypeVars(rawWrapperType, - locator); - cs.addConstraint(ConstraintKind::Equal, wrapperType, wrappedValueType, - locator); - cs.setContextualType(typeRepr, TypeLoc::withoutLoc(wrappedValueType), - CTP_ComposedPropertyWrapper); - } - - wrappedValueType = wrapperType->getTypeOfMember( - dc->getParentModule(), wrapperInfo.valueVar); - } - - // The property type must be equal to the wrapped value type - cs.addConstraint(ConstraintKind::Equal, propertyType, wrappedValueType, - cs.getConstraintLocator(wrappedVar, LocatorPathElt::ContextualType())); - cs.setContextualType(wrappedVar, TypeLoc::withoutLoc(wrappedValueType), - CTP_WrappedProperty); - return false; -} - /// Generate additional constraints for the pattern of an initialization. static bool generateInitPatternConstraints( ConstraintSystem &cs, SolutionApplicationTarget target, Expr *initializer) { diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 62fcb75179a06..ae9dd1b36dc35 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -1415,6 +1415,12 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( cs, cs.getConstraintLocator(loc))); } + if (auto *param = paramInfo.getPropertyWrapperParam(argIdx)) { + return cs.matchPropertyWrapperArgument( + /*wrapperType=*/paramTy, /*wrappedValueType=*/argTy, param, + subKind, loc); + } + // If argument comes for declaration it should loose // `@autoclosure` flag, because in context it's used // as a function type represented by autoclosure. @@ -1436,6 +1442,27 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( return cs.getTypeMatchSuccess(); } +ConstraintSystem::TypeMatchResult +ConstraintSystem::matchPropertyWrapperArgument(Type wrapperType, Type wrappedValueArgumentType, + const ParamDecl *param, ConstraintKind matchKind, + ConstraintLocatorBuilder locator) { + auto anchor = simplifyLocatorToAnchor(getConstraintLocator(locator)); + if (!anchor) + return getTypeMatchFailure(locator); + + auto *arg = getAsExpr(anchor); + auto initializer = buildPropertyWrapperWrappedValueCall(param, wrapperType, arg, + /*ignoreAttributeArgs=*/false); + appliedPropertyWrappers[arg] = initializer; + + generateConstraints(initializer, DC); + addConstraint(matchKind, getType(initializer), wrapperType, + getConstraintLocator(initializer)); + + // FIXME: Can there be failures from the above? + return getTypeMatchSuccess(); +} + ConstraintSystem::TypeMatchResult ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2, ConstraintKind kind, TypeMatchOptions flags, @@ -8155,6 +8182,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. @@ -8164,7 +8192,7 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, } 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]`, @@ -8196,6 +8224,29 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, param.isVariadic() ? ArraySliceType::get(typeVar) : Type(typeVar); auto externalType = param.getOldType(); + + if (auto wrapperInfo = paramDecl->getPropertyWrapperBackingPropertyInfo()) { + auto bindPropertyType = [&](VarDecl *var, Type type) { + auto *typeVar = createTypeVariable(paramLoc, 0); + auto constraintKind = (oneWayConstraints ? + ConstraintKind::OneWayEqual : ConstraintKind::Equal); + addConstraint(constraintKind, typeVar, type, paramLoc); + setType(var, typeVar); + }; + + bindPropertyType(wrapperInfo.backingVar, externalType); + + if (auto *projection = wrapperInfo.projectionVar) { + auto typeInfo = paramDecl->getAttachedPropertyWrapperTypeInfo(0); + auto projectionType = externalType->getTypeOfMember(paramDecl->getModuleContext(), + typeInfo.projectedValueVar); + bindPropertyType(projection, projectionType); + } + + // Bind the internal parameter type to the wrapped value type + externalType = swift::computeWrappedValueType(paramDecl, externalType); + } + if (oneWayConstraints) { addConstraint( ConstraintKind::OneWayBindParam, typeVar, externalType, paramLoc); @@ -8205,7 +8256,7 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, } } - setType(paramList->get(i), internalType); + setType(paramDecl, internalType); parameters.push_back(param); } 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/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index d87aaedff1bae..ba06e2de3589a 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 @@ -1690,6 +1691,20 @@ void TypeChecker::coerceParameterListToType(ParameterList *P, ClosureExpr *CE, // trying to coerce argument to contextual type would mean erasing // valuable diagnostic information. if (isValidType(ty) || shouldOverwriteParam(param)) { + // Apply property wrapper types to synthesized vars + if (auto wrapperInfo = param->getPropertyWrapperBackingPropertyInfo()) { + wrapperInfo.backingVar->setInterfaceType(ty->mapTypeOutOfContext()); + + if (auto *projection = wrapperInfo.projectionVar) { + auto typeInfo = param->getAttachedPropertyWrapperTypeInfo(0); + auto projectionType = ty->getTypeOfMember(param->getModuleContext(), + typeInfo.projectedValueVar); + projection->setInterfaceType(projectionType->mapTypeOutOfContext()); + } + + ty = computeWrappedValueType(param, ty); + } + param->setInterfaceType(ty->mapTypeOutOfContext()); } }; diff --git a/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index dfe7702d9f94f..acde9d75a6bdf 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -562,7 +562,7 @@ 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(); @@ -589,7 +589,7 @@ Type swift::computeWrappedValueType(VarDecl *var, Type backingStorageType, } Expr *swift::buildPropertyWrapperWrappedValueCall( - VarDecl *var, Type backingStorageType, Expr *value, bool ignoreAttributeArgs, + const VarDecl *var, Type backingStorageType, Expr *value, bool ignoreAttributeArgs, llvm::function_ref innermostInitCallback) { // From the innermost wrapper type out, form init(wrapperValue:) calls. ASTContext &ctx = var->getASTContext(); diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 1d1e34a41158d..aa109e5c85920 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -1245,7 +1245,7 @@ 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 @@ -1253,7 +1253,7 @@ Type computeWrappedValueType(VarDecl *var, Type backingStorageType, /// pass a callback that will get invoked with the innermost init(wrappedValue:) /// call. Expr *buildPropertyWrapperWrappedValueCall( - VarDecl *var, Type backingStorageType, Expr *value, bool ignoreAttributeArgs, + const VarDecl *var, Type backingStorageType, Expr *value, bool ignoreAttributeArgs, llvm::function_ref callback = [](ApplyExpr *) {}); /// Whether an overriding declaration requires the 'override' keyword. From f57180a68d689eeef6e8eae3a02f20f84782d8f4 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 9 Oct 2020 21:01:53 -0700 Subject: [PATCH 03/38] [Property Wrappers] Don't allow property wrapper attributes to have arguments when applied to parameters. --- include/swift/AST/DiagnosticsSema.def | 3 +++ lib/Sema/TypeCheckAttr.cpp | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 52e0c70bbcb04..1d6df952612fd 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5484,6 +5484,9 @@ ERROR(property_wrapper_attribute_not_on_property, none, (Identifier)) NOTE(property_wrapper_declared_here,none, "property wrapper type %0 declared here", (Identifier)) +ERROR(property_wrapper_param_arg, none, + "property wrapper attributes applied to parameters cannot have arguments", + ()) ERROR(property_wrapper_mutating_get_composed_to_get_only,none, "property wrapper %0 with a mutating getter cannot be composed inside " diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 659d875e6727e..89289ee8c8abf 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2995,6 +2995,12 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { return; } + if (isa(D) && attr->getArg()) { + diagnose(attr->getArg()->getLoc(), diag::property_wrapper_param_arg); + attr->setInvalid(); + return; + } + return; } From d9951bed23ea9e7347bac358820f0cd719ab4f3a Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Sun, 11 Oct 2020 13:10:32 -0700 Subject: [PATCH 04/38] [Property Wrappers] Add a request to synthesize the local wrapped value variable for a parameter with an attached property wrapper. --- include/swift/AST/Decl.h | 4 ++++ include/swift/AST/TypeCheckRequests.h | 20 +++++++++++++++++ include/swift/AST/TypeCheckerTypeIDZone.def | 3 +++ lib/AST/Decl.cpp | 14 +++++++++++- lib/AST/TypeCheckRequests.cpp | 5 +++++ lib/AST/UnqualifiedLookup.cpp | 4 +++- lib/Sema/TypeCheckStorage.cpp | 24 +++++++++++++++++++++ 7 files changed, 72 insertions(+), 2 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 9a222dff5d02a..7eb8a259941d8 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -4978,6 +4978,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/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(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()) { @@ -5963,6 +5972,9 @@ void VarDecl::visitAuxiliaryDecls(llvm::function_ref visit) con if (auto *projectionVar = getPropertyWrapperProjectionVar()) visit(projectionVar); + + if (auto *wrappedValueVar = getPropertyWrapperWrappedValueVar()) + visit(wrappedValueVar); } } diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 08d9129adf20b..7f7b1e781641e 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -493,6 +493,11 @@ bool PropertyWrapperBackingPropertyInfoRequest::isCached() const { return !var->getAttrs().isEmpty(); } +bool PropertyWrapperWrappedValueVarRequest::isCached() const { + auto var = std::get<0>(getStorage()); + return !var->getAttrs().isEmpty(); +} + bool PropertyWrapperMutabilityRequest::isCached() const { auto var = std::get<0>(getStorage()); return !var->getAttrs().isEmpty(); 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/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 311b1a25b059d..d32876577a03c 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -2833,6 +2833,30 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator, initializer, wrappedValue); } +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(); + VarDecl *localVar = new (ctx) VarDecl(/*IsStatic=*/false, + VarDecl::Introducer::Var, + var->getLoc(), + var->getName(), dc); + localVar->setInterfaceType(var->getInterfaceType()); + localVar->setImplicit(); + localVar->getAttrs() = var->getAttrs(); + localVar->overwriteAccess(var->getFormalAccess()); + localVar->setImplInfo(StorageImplInfo::getImmutableComputed()); + + evaluator.cacheOutput(PropertyWrapperBackingPropertyInfoRequest{localVar}, + std::move(wrapperInfo)); + return localVar; +} + /// Given a storage declaration in a protocol, set it up with the right /// StorageImpl and add the right set of opaque accessors. static void finishProtocolStorageImplInfo(AbstractStorageDecl *storage, From 5bdb4dc8357e244088700ae6daef1e5375052ee8 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Sun, 11 Oct 2020 15:24:49 -0700 Subject: [PATCH 05/38] [Sema] Visit auxiliary decls on parameters in the decl checker. --- lib/Sema/TypeCheckDeclPrimary.cpp | 66 +++++++++++++++++-------------- lib/Sema/TypeCheckPattern.cpp | 2 + lib/Sema/TypeCheckStorage.cpp | 7 ++-- 3 files changed, 42 insertions(+), 33 deletions(-) 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/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index ba06e2de3589a..623ae65fe1861 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -1721,4 +1721,6 @@ void TypeChecker::coerceParameterListToType(ParameterList *P, ClosureExpr *CE, params[i].isInOut()); assert(!param->isDefaultArgument() && "Closures cannot have default args"); } + + TypeChecker::checkParameterList(P, CE); } diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index d32876577a03c..a537f308a0825 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -2485,9 +2485,10 @@ static VarDecl *synthesizePropertyWrapperProjectionVar( 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; } From c5bed94843be70087d415b6dc33f6bcff261130d Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Sun, 11 Oct 2020 15:26:55 -0700 Subject: [PATCH 06/38] [SILGen] Teach SILGen to emit property wrapper parameters. --- lib/SILGen/SILGenFunction.cpp | 11 +++++++++++ lib/SILGen/SILGenProlog.cpp | 3 +++ 2 files changed, 14 insertions(+) diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 4c8dd14891328..b28ccf8b2217b 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -516,6 +516,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 +599,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 { diff --git a/lib/SILGen/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index 970f45ba5088a..9e9412c9ca052 100644 --- a/lib/SILGen/SILGenProlog.cpp +++ b/lib/SILGen/SILGenProlog.cpp @@ -242,6 +242,9 @@ struct ArgumentInitHelper { } void emitParam(ParamDecl *PD) { + if (auto *backingVar = PD->getPropertyWrapperBackingProperty()) + PD = cast(backingVar); + auto type = PD->getType(); assert(type->isMaterializable()); From aa573c0f3e632ae5f9d86c1be02e100d42e69063 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Sun, 11 Oct 2020 15:30:19 -0700 Subject: [PATCH 07/38] [Property Wrappers] Allow property wrapper attributes to be applied to parameters. --- lib/Sema/TypeCheckAttr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 89289ee8c8abf..24821417f0500 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()); From 4bdeed570b2e1de1d8b63af650e2f192407ab9be Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 12 Oct 2020 11:41:14 -0700 Subject: [PATCH 08/38] [ConstraintSystem] Use a wrapped value placeholder when matching property wrapper arguments in the constraint system in order to avoid generating constraints for the argument multiple times. --- lib/AST/ASTWalker.cpp | 10 ++++++---- lib/AST/Expr.cpp | 5 +++-- lib/Sema/CSApply.cpp | 6 ++++++ lib/Sema/CSGen.cpp | 6 +++++- lib/Sema/CSSimplify.cpp | 13 +++++++++++-- 5 files changed, 31 insertions(+), 9 deletions(-) diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index 52ddd8d693add..54fa39fdc87d9 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -495,10 +495,12 @@ class Traversal : public ASTVisitorgetOpaqueValuePlaceholder())) - E->setOpaqueValuePlaceholder(dyn_cast(placeholder)); - else - return nullptr; + if (E->getOpaqueValuePlaceholder()) { + if (auto *placeholder = doIt(E->getOpaqueValuePlaceholder())) + E->setOpaqueValuePlaceholder(dyn_cast(placeholder)); + else + return nullptr; + } if (E->getOriginalWrappedValue()) { if (auto *newValue = doIt(E->getOriginalWrappedValue())) diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index ab3d54bf7ae4b..0fde5141de313 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1499,8 +1499,9 @@ 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); diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 1254abf42e3ae..1557d5d3e0b93 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -3437,6 +3437,12 @@ 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; } diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 2ad86c5c986cd..d8c47ca9a73b6 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2666,7 +2666,11 @@ namespace { Type visitPropertyWrapperValuePlaceholderExpr( PropertyWrapperValuePlaceholderExpr *expr) { - return expr->getType(); + if (auto ty = expr->getType()) + return ty; + + assert(CS.getType(expr)); + return CS.getType(expr); } Type visitDefaultArgumentExpr(DefaultArgumentExpr *expr) { diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index ae9dd1b36dc35..83043552b6558 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -1450,15 +1450,24 @@ ConstraintSystem::matchPropertyWrapperArgument(Type wrapperType, Type wrappedVal if (!anchor) return getTypeMatchFailure(locator); - auto *arg = getAsExpr(anchor); - auto initializer = buildPropertyWrapperWrappedValueCall(param, wrapperType, arg, + // Use a wrapped value placeholder to avoid generating constriants for the + // argument expression again. + auto *placeholder = PropertyWrapperValuePlaceholderExpr::create( + getASTContext(), param->getSourceRange(), Type(), /*wrappedValue=*/nullptr); + setType(placeholder, wrappedValueArgumentType); + auto initializer = buildPropertyWrapperWrappedValueCall(param, wrapperType, placeholder, /*ignoreAttributeArgs=*/false); + auto *arg = getAsExpr(anchor); appliedPropertyWrappers[arg] = initializer; generateConstraints(initializer, DC); addConstraint(matchKind, getType(initializer), wrapperType, getConstraintLocator(initializer)); + // Set the original wrapped value _after_ generating constraints + // so the argument expression isn't visited in CSGen. + placeholder->setOriginalWrappedValue(arg); + // FIXME: Can there be failures from the above? return getTypeMatchSuccess(); } From cf25fff919f1c2f525f642b11107392b2075fd04 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Sun, 8 Nov 2020 18:41:14 -0500 Subject: [PATCH 09/38] [Property Wrappers] Set the interface type of the synthesized local wrapped value var in CSApply. This is necessary when closure parameter types are inferred. --- lib/Sema/TypeCheckPattern.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index 623ae65fe1861..6cf7683e51412 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -1703,6 +1703,7 @@ void TypeChecker::coerceParameterListToType(ParameterList *P, ClosureExpr *CE, } ty = computeWrappedValueType(param, ty); + param->getPropertyWrapperWrappedValueVar()->setInterfaceType(ty); } param->setInterfaceType(ty->mapTypeOutOfContext()); From 8bdc8f9dde06085224ec5780c8c54f52fcc6cb94 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 9 Nov 2020 21:51:13 -0500 Subject: [PATCH 10/38] [Property Wrappers] Use the TypeExpr instead of the TypeRepr of the property wrapper custom attribute to get the backing wrapper type in CSApply. This is necessary because implicit custom attributes do not have TypeReprs, but they always have TypeExprs. --- include/swift/AST/Attr.h | 1 + lib/Sema/CSApply.cpp | 2 +- lib/Sema/CSGen.cpp | 7 ++++--- 3 files changed, 6 insertions(+), 4 deletions(-) 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/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 1557d5d3e0b93..99db27c4cc3e9 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -8217,7 +8217,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/CSGen.cpp b/lib/Sema/CSGen.cpp index d8c47ca9a73b6..7bdf113a57913 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3904,10 +3904,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()) From 9afa7ba55de173bd7106a03ace238bd1730aeef5 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 10 Nov 2020 22:15:30 -0500 Subject: [PATCH 11/38] [Property Wrappers] Determine the mutability of a property wrapper parameter based on mutability of the property wrapper's wrappedValue setter. --- lib/Sema/TypeCheckStorage.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index a537f308a0825..2b73e7a410166 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -2851,7 +2851,13 @@ PropertyWrapperWrappedValueVarRequest::evaluate(Evaluator &evaluator, localVar->setImplicit(); localVar->getAttrs() = var->getAttrs(); localVar->overwriteAccess(var->getFormalAccess()); - localVar->setImplInfo(StorageImplInfo::getImmutableComputed()); + + auto mutability = *var->getPropertyWrapperMutability(); + if (mutability.Setter == PropertyWrapperMutability::Nonmutating) { + localVar->setImplInfo(StorageImplInfo::getMutableComputed()); + } else { + localVar->setImplInfo(StorageImplInfo::getImmutableComputed()); + } evaluator.cacheOutput(PropertyWrapperBackingPropertyInfoRequest{localVar}, std::move(wrapperInfo)); From 19a4f93586807d0bbf2370ea120d3ce21401d124 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 14 Jan 2021 12:36:45 -0800 Subject: [PATCH 12/38] [Property Wrappers] Teach the constraint system to build init(projectedValue:) calls. --- include/swift/AST/PropertyWrappers.h | 13 ++++++++ include/swift/Sema/ConstraintSystem.h | 3 +- lib/Sema/CSSimplify.cpp | 34 ++++++++++++++------ lib/Sema/ConstraintSystem.cpp | 4 +-- lib/Sema/TypeCheckPropertyWrapper.cpp | 45 +++++++++++++-------------- lib/Sema/TypeChecker.h | 16 ++++++---- 6 files changed, 73 insertions(+), 42 deletions(-) diff --git a/include/swift/AST/PropertyWrappers.h b/include/swift/AST/PropertyWrappers.h index 2f78ad4123d76..53af955e87beb 100644 --- a/include/swift/AST/PropertyWrappers.h +++ b/include/swift/AST/PropertyWrappers.h @@ -29,6 +29,19 @@ 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 +}; + /// Describes a property wrapper type. struct PropertyWrapperTypeInfo { /// The property through which access that uses this wrapper type is diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 709117559adb9..999c8925e7bf9 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -4647,7 +4647,8 @@ class ConstraintSystem { /// expression. TypeMatchResult matchPropertyWrapperArgument( Type wrapperType, Type wrappedValueArgumentType, const ParamDecl *param, - ConstraintKind matchKind, ConstraintLocatorBuilder locator); + Identifier argLabel, ConstraintKind matchKind, + ConstraintLocatorBuilder locator); Optional determineBestBindings(); diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 83043552b6558..2cc6632dd31e9 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) @@ -1417,8 +1422,8 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( if (auto *param = paramInfo.getPropertyWrapperParam(argIdx)) { return cs.matchPropertyWrapperArgument( - /*wrapperType=*/paramTy, /*wrappedValueType=*/argTy, param, - subKind, loc); + /*wrapperType=*/paramTy, /*argumentType=*/argTy, param, + argInfo->Labels[argIdx], subKind, loc); } // If argument comes for declaration it should loose @@ -1443,9 +1448,10 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( } ConstraintSystem::TypeMatchResult -ConstraintSystem::matchPropertyWrapperArgument(Type wrapperType, Type wrappedValueArgumentType, - const ParamDecl *param, ConstraintKind matchKind, - ConstraintLocatorBuilder locator) { +ConstraintSystem::matchPropertyWrapperArgument( + Type wrapperType, Type argumentType, const ParamDecl *param, + Identifier argLabel, ConstraintKind matchKind, + ConstraintLocatorBuilder locator) { auto anchor = simplifyLocatorToAnchor(getConstraintLocator(locator)); if (!anchor) return getTypeMatchFailure(locator); @@ -1454,9 +1460,17 @@ ConstraintSystem::matchPropertyWrapperArgument(Type wrapperType, Type wrappedVal // argument expression again. auto *placeholder = PropertyWrapperValuePlaceholderExpr::create( getASTContext(), param->getSourceRange(), Type(), /*wrappedValue=*/nullptr); - setType(placeholder, wrappedValueArgumentType); - auto initializer = buildPropertyWrapperWrappedValueCall(param, wrapperType, placeholder, - /*ignoreAttributeArgs=*/false); + setType(placeholder, argumentType); + + PropertyWrapperInitKind initKind; + if (!argLabel.empty() && argLabel.str().startswith("$")) { + initKind = PropertyWrapperInitKind::ProjectedValue; + } else { + initKind = PropertyWrapperInitKind::WrappedValue; + } + + auto initializer = buildPropertyWrapperInitCall( + param, wrapperType, placeholder, initKind); auto *arg = getAsExpr(anchor); appliedPropertyWrappers[arg] = initializer; diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 8a285537ea981..53b8fcfdd0684 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -4907,8 +4907,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/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index acde9d75a6bdf..0e9171af283d9 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; @@ -588,8 +581,9 @@ Type swift::computeWrappedValueType(const VarDecl *var, Type backingStorageType, return wrappedValueType; } -Expr *swift::buildPropertyWrapperWrappedValueCall( - const VarDecl *var, Type backingStorageType, Expr *value, bool ignoreAttributeArgs, +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(); @@ -617,17 +611,22 @@ 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; - switch (var->getAttachedPropertyWrapperTypeInfo(i).wrappedValueInit) { - case PropertyWrapperTypeInfo::HasInitialValueInit: - argName = ctx.Id_initialValue; - break; + if (initKind == PropertyWrapperInitKind::ProjectedValue) { + argName = ctx.Id_projectedValue; + } else { + assert(initKind == PropertyWrapperInitKind::WrappedValue); + switch (var->getAttachedPropertyWrapperTypeInfo(i).wrappedValueInit) { + case PropertyWrapperTypeInfo::HasInitialValueInit: + argName = ctx.Id_initialValue; + break; - case PropertyWrapperTypeInfo::HasWrappedValueInit: - case PropertyWrapperTypeInfo::NoWrappedValueInit: - argName = ctx.Id_wrappedValue; - break; + case PropertyWrapperTypeInfo::HasWrappedValueInit: + case PropertyWrapperTypeInfo::NoWrappedValueInit: + argName = ctx.Id_wrappedValue; + break; + } } auto endLoc = initializer->getEndLoc(); diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index aa109e5c85920..902851a37fbbe 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" @@ -1248,12 +1249,15 @@ bool isValidKeyPathDynamicMemberLookup(SubscriptDecl *decl, 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( - const VarDecl *var, Type backingStorageType, Expr *value, bool ignoreAttributeArgs, +/// 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. From 6aee012d841f807642518570cdd3bd5113d70ba9 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 15 Feb 2021 22:14:49 -0800 Subject: [PATCH 13/38] [CSApply] Add buildPropertyWrapperFnThunk, which builds a thunk around a reference to a function with property wrapper parameters and initializes each backing property wrapper from a wrapped or projected value. --- lib/Sema/CSApply.cpp | 97 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 99db27c4cc3e9..bf650fc820f7d 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -965,6 +965,103 @@ namespace { return false; } + AutoClosureExpr *buildPropertyWrapperFnThunk( + Expr *fnRef, FunctionType *fnType, AnyFunctionRef fnDecl, + 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. + Expr *init = appliedPropertyWrappers[appliedWrapperIndex++]; + auto *placeholder = findWrappedValuePlaceholder(init); + placeholder->setOriginalWrappedValue(paramRef); + + auto target = SolutionApplicationTarget(init, cs.DC, CTP_Unused, Type(), + /*isDiscarded=*/false); + auto result = cs.applySolution(solution, target); + if (!result) + return nullptr; + + paramRef = result->getAsExpr(); + 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, From 78b202ccdf43b1121b9be0345fda7dc895ae8817 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 15 Feb 2021 22:19:06 -0800 Subject: [PATCH 14/38] [ASTWalker] Support configurating whether or not to visit the original value of a property wrapper placeholder expression. --- include/swift/AST/ASTWalker.h | 4 ++++ lib/AST/ASTWalker.cpp | 12 +++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) 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/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index 54fa39fdc87d9..defe38e63b5c2 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -502,11 +502,13 @@ class Traversal : public ASTVisitorgetOriginalWrappedValue()) { - if (auto *newValue = doIt(E->getOriginalWrappedValue())) - E->setOriginalWrappedValue(newValue); - else - return nullptr; + if (Walker.shouldWalkIntoPropertyWrapperPlaceholderValue()) { + if (E->getOriginalWrappedValue()) { + if (auto *newValue = doIt(E->getOriginalWrappedValue())) + E->setOriginalWrappedValue(newValue); + else + return nullptr; + } } return E; From 31f7b1ac7504d5cd65b71e9ec574f02cfbe9884f Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 15 Feb 2021 22:23:15 -0800 Subject: [PATCH 15/38] [ConstraintSystem] Change the type of an unapplied function reference when it has property wrapper parameters. The property wrapper type will be replaced with either the wrapped-value or projected-value type, depending on the argument label/parameter name, and CSApply will build a thunk to construct the property wrapper and call the function. --- include/swift/AST/AnyFunctionRef.h | 13 +++ include/swift/AST/DiagnosticsSema.def | 3 + include/swift/Sema/ConstraintSystem.h | 20 ++--- lib/Sema/CSApply.cpp | 123 ++++++++++++++++++++++---- lib/Sema/CSClosure.cpp | 2 +- lib/Sema/CSGen.cpp | 46 ++++++++-- lib/Sema/CSSimplify.cpp | 90 +++++-------------- lib/Sema/ConstraintSystem.cpp | 70 +++++++++++++++ lib/Sema/TypeCheckPattern.cpp | 19 +--- lib/Sema/TypeChecker.h | 3 +- 10 files changed, 267 insertions(+), 122 deletions(-) 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/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 1d6df952612fd..ae1d83b8ed0d3 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5487,6 +5487,9 @@ NOTE(property_wrapper_declared_here,none, ERROR(property_wrapper_param_arg, none, "property wrapper attributes applied to parameters cannot have arguments", ()) +ERROR(property_wrapper_param_no_projection,none, + "cannot use property wrapper projection parameter; " + "wrapper %0 does not have a 'projectedValue'", (Type)) ERROR(property_wrapper_mutating_get_composed_to_get_only,none, "property wrapper %0 with a mutating getter cannot be composed inside " diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 999c8925e7bf9..0e4e8d003d339 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -1187,7 +1187,7 @@ class Solution { resultBuilderTransformed; /// A map from argument expressions to their applied property wrapper expressions. - llvm::MapVector appliedPropertyWrappers; + llvm::MapVector> appliedPropertyWrappers; /// Simplify the given type by substituting all occurrences of /// type variables for their fixed types. @@ -2233,13 +2233,13 @@ class ConstraintSystem { std::vector> resultBuilderTransformed; - /// A map from argument expressions to their applied property wrapper expressions. - llvm::SmallMapVector appliedPropertyWrappers; - /// Cache of the effects any closures visited. 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; @@ -4642,13 +4642,11 @@ class ConstraintSystem { ConstraintKind bodyResultConstraintKind, ConstraintLocatorBuilder locator); - /// Matches a wrapped value argument type to its backing wrapper type by applying - /// the property wrapper and generating constraints for the backing initializer - /// expression. - TypeMatchResult matchPropertyWrapperArgument( - Type wrapperType, Type wrappedValueArgumentType, const ParamDecl *param, - Identifier argLabel, ConstraintKind matchKind, - ConstraintLocatorBuilder locator); + /// Matches a wrapped or projected value parameter type to its backing + /// property wrapper type by applying the property wrapper. + void applyPropertyWrapperParameter( + Type wrapperType, Type paramType, ParamDecl *param, Identifier argLabel, + ConstraintKind matchKind, ConstraintLocatorBuilder locator); Optional determineBestBindings(); diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index bf650fc820f7d..aaa93f96f3a0d 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, + appliedWrappers); + } + } + + return result; } /// Describes an opened existential that has not yet been closed. @@ -1096,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(); @@ -1144,6 +1157,19 @@ namespace { if (selfParamRef->isSuperExpr()) selfCall->setIsSuper(true); + auto &appliedWrappers = solution.appliedPropertyWrappers[locator.getAnchor()]; + if (!appliedWrappers.empty()) { + auto fnDecl = AnyFunctionRef(dyn_cast(member)); + auto *closure = buildPropertyWrapperFnThunk(selfCall, calleeFnType, + fnDecl, 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; @@ -1809,7 +1835,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. @@ -2015,10 +2042,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; @@ -2840,7 +2869,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); @@ -5028,7 +5058,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 @@ -5564,9 +5594,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 = @@ -5796,6 +5828,22 @@ Expr *ExprRewriter::coerceCallArguments( return true; }; + if (paramInfo.getPropertyWrapperParam(paramIdx)) { + Expr *init = appliedPropertyWrappers[appliedWrapperIndex++]; + auto *placeholder = findWrappedValuePlaceholder(init); + placeholder->setOriginalWrappedValue(arg); + + // Apply the solution to the property wrapper initializer. + auto target = SolutionApplicationTarget(init, cs.DC, CTP_Unused, Type(), + /*isDiscarded=*/false); + auto result = cs.applySolution(solution, target); + if (!result) + return nullptr; + + arg = result->getAsExpr(); + cs.cacheExprTypes(arg); + } + if (argRequiresAutoClosureExpr(param, argType)) { assert(!param.isInOut()); @@ -7233,12 +7281,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; } @@ -7438,10 +7488,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; } @@ -7710,6 +7762,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; } @@ -7719,17 +7777,14 @@ namespace { } std::pair walkToExprPre(Expr *expr) override { - // If this expression has an applied property wrapper, get the backing - // initializer expression. - if (Rewriter.solution.appliedPropertyWrappers.count(expr)) { - auto *init = Rewriter.solution.appliedPropertyWrappers[expr]; - Rewriter.solution.appliedPropertyWrappers.erase(expr); - expr = init; - } - // 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 }; } @@ -7766,6 +7821,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; + + auto wrapperInfo = param->getPropertyWrapperBackingPropertyInfo(); + auto *backingVar = wrapperInfo.backingVar; + auto wrapperType = solution.simplifyType(solution.getType(backingVar)); + backingVar->setInterfaceType(wrapperType->mapTypeOutOfContext()); + + if (auto *projection = wrapperInfo.projectionVar) { + auto typeInfo = param->getAttachedPropertyWrapperTypeInfo(0); + auto projectionType = wrapperType->getTypeOfMember(param->getModuleContext(), + typeInfo.projectedValueVar); + projection->setInterfaceType(projectionType); + } + + auto *wrappedValueVar = param->getPropertyWrapperWrappedValueVar(); + auto wrappedValueType = computeWrappedValueType(param, wrapperType); + wrappedValueVar->setInterfaceType(wrappedValueType); + } + + auto &appliedWrappers = Rewriter.solution.appliedPropertyWrappers[closure]; + return Rewriter.buildPropertyWrapperFnThunk(closure, closureFnType, closure, + appliedWrappers); + } + /// Rewrite the function for the given solution. /// /// \returns true if an error occurred. 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/CSGen.cpp b/lib/Sema/CSGen.cpp index 7bdf113a57913..df47cf47a5361 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2053,16 +2053,16 @@ namespace { TVO_CanBindToInOut | TVO_CanBindToNoEscape | TVO_CanBindToHole); } - if (param->hasAttachedPropertyWrapper()) { + if (auto wrapperInfo = param->getPropertyWrapperBackingPropertyInfo()) { auto *wrapperAttr = param->getAttachedPropertyWrappers().front(); auto wrapperType = param->getAttachedPropertyWrapperType(0); auto backingType = CS.openUnboundGenericTypes( wrapperType, CS.getConstraintLocator(wrapperAttr->getTypeRepr())); - if (generateWrappedPropertyTypeConstraints(CS, backingType, param, externalType)) - return nullptr; + CS.setType(wrapperInfo.backingVar, backingType); - // The external parameter type is the backing property wrapper type - externalType = backingType; + CS.applyPropertyWrapperParameter(backingType, externalType, param, + param->getName(), ConstraintKind::Equal, + CS.getConstraintLocator(closure)); } closureParams.push_back(param->toFunctionParam(externalType)); @@ -2666,8 +2666,10 @@ namespace { Type visitPropertyWrapperValuePlaceholderExpr( PropertyWrapperValuePlaceholderExpr *expr) { - if (auto ty = expr->getType()) + if (auto ty = expr->getType()) { + CS.cacheType(expr); return ty; + } assert(CS.getType(expr)); return CS.getType(expr); @@ -4057,6 +4059,38 @@ bool ConstraintSystem::generateConstraints( return false; } +void ConstraintSystem::applyPropertyWrapperParameter( + 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(); + } + + PropertyWrapperInitKind initKind; + if (argLabel.hasDollarPrefix()) { + auto typeInfo = param->getAttachedPropertyWrapperTypeInfo(0); + auto projectionType = wrapperType->getTypeOfMember(param->getModuleContext(), + typeInfo.projectedValueVar); + addConstraint(matchKind, paramType, projectionType, locator); + initKind = PropertyWrapperInitKind::ProjectedValue; + } else { + generateWrappedPropertyTypeConstraints(*this, wrapperType, param, paramType); + initKind = PropertyWrapperInitKind::WrappedValue; + } + + // Build the wrapper initializer expression and generate constraints. + auto *placeholder = PropertyWrapperValuePlaceholderExpr::create( + getASTContext(), anchor->getSourceRange(), Type(), /*wrappedValue*/nullptr); + setType(placeholder, paramType); + + auto *init = buildPropertyWrapperInitCall(param, wrapperType, placeholder, initKind); + setType(init, wrapperType); + generateConstraints(init, DC); + + appliedPropertyWrappers[anchor].push_back({ init }); +} + void ConstraintSystem::optimizeConstraints(Expr *e) { if (getASTContext().TypeCheckerOpts.DisableConstraintSolverPerformanceHacks) return; diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 2cc6632dd31e9..31cf01ec350c0 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -1421,9 +1421,10 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( } if (auto *param = paramInfo.getPropertyWrapperParam(argIdx)) { - return cs.matchPropertyWrapperArgument( - /*wrapperType=*/paramTy, /*argumentType=*/argTy, param, - argInfo->Labels[argIdx], subKind, loc); + auto argLabel = argInfo->Labels[argIdx]; + cs.applyPropertyWrapperParameter(paramTy, argTy, const_cast(param), + argLabel, subKind, locator); + continue; } // If argument comes for declaration it should loose @@ -1447,45 +1448,6 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( return cs.getTypeMatchSuccess(); } -ConstraintSystem::TypeMatchResult -ConstraintSystem::matchPropertyWrapperArgument( - Type wrapperType, Type argumentType, const ParamDecl *param, - Identifier argLabel, ConstraintKind matchKind, - ConstraintLocatorBuilder locator) { - auto anchor = simplifyLocatorToAnchor(getConstraintLocator(locator)); - if (!anchor) - return getTypeMatchFailure(locator); - - // Use a wrapped value placeholder to avoid generating constriants for the - // argument expression again. - auto *placeholder = PropertyWrapperValuePlaceholderExpr::create( - getASTContext(), param->getSourceRange(), Type(), /*wrappedValue=*/nullptr); - setType(placeholder, argumentType); - - PropertyWrapperInitKind initKind; - if (!argLabel.empty() && argLabel.str().startswith("$")) { - initKind = PropertyWrapperInitKind::ProjectedValue; - } else { - initKind = PropertyWrapperInitKind::WrappedValue; - } - - auto initializer = buildPropertyWrapperInitCall( - param, wrapperType, placeholder, initKind); - auto *arg = getAsExpr(anchor); - appliedPropertyWrappers[arg] = initializer; - - generateConstraints(initializer, DC); - addConstraint(matchKind, getType(initializer), wrapperType, - getConstraintLocator(initializer)); - - // Set the original wrapped value _after_ generating constraints - // so the argument expression isn't visited in CSGen. - placeholder->setOriginalWrappedValue(arg); - - // FIXME: Can there be failures from the above? - return getTypeMatchSuccess(); -} - ConstraintSystem::TypeMatchResult ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2, ConstraintKind kind, TypeMatchOptions flags, @@ -6736,8 +6698,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()). @@ -8247,29 +8228,6 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, param.isVariadic() ? ArraySliceType::get(typeVar) : Type(typeVar); auto externalType = param.getOldType(); - - if (auto wrapperInfo = paramDecl->getPropertyWrapperBackingPropertyInfo()) { - auto bindPropertyType = [&](VarDecl *var, Type type) { - auto *typeVar = createTypeVariable(paramLoc, 0); - auto constraintKind = (oneWayConstraints ? - ConstraintKind::OneWayEqual : ConstraintKind::Equal); - addConstraint(constraintKind, typeVar, type, paramLoc); - setType(var, typeVar); - }; - - bindPropertyType(wrapperInfo.backingVar, externalType); - - if (auto *projection = wrapperInfo.projectionVar) { - auto typeInfo = paramDecl->getAttachedPropertyWrapperTypeInfo(0); - auto projectionType = externalType->getTypeOfMember(paramDecl->getModuleContext(), - typeInfo.projectedValueVar); - bindPropertyType(projection, projectionType); - } - - // Bind the internal parameter type to the wrapped value type - externalType = swift::computeWrappedValueType(paramDecl, externalType); - } - if (oneWayConstraints) { addConstraint( ConstraintKind::OneWayBindParam, typeVar, externalType, paramLoc); diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 53b8fcfdd0684..d8f9211e38175 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -1156,6 +1156,62 @@ 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 + // with wrapped parameters. + if (!AnyFunctionRef(funcDecl).hasPropertyWrapperParameters() || + !(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)) { + auto *paramDecl = paramList->get(i); + if (!paramDecl->hasAttachedPropertyWrapper()) { + adjustedParamTypes.push_back(paramTypes[i]); + continue; + } + + 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 *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.applyPropertyWrapperParameter(paramType, wrappedType, paramDecl, argLabel, + ConstraintKind::Equal, locator); + } + + return FunctionType::get(adjustedParamTypes, functionType->getResult()); +} + std::pair ConstraintSystem::getTypeOfReference(ValueDecl *value, FunctionRefKind functionRefKind, @@ -1201,6 +1257,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 +1691,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; diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index 6cf7683e51412..4e1e98e7ccf48 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -1665,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 @@ -1691,21 +1691,6 @@ void TypeChecker::coerceParameterListToType(ParameterList *P, ClosureExpr *CE, // trying to coerce argument to contextual type would mean erasing // valuable diagnostic information. if (isValidType(ty) || shouldOverwriteParam(param)) { - // Apply property wrapper types to synthesized vars - if (auto wrapperInfo = param->getPropertyWrapperBackingPropertyInfo()) { - wrapperInfo.backingVar->setInterfaceType(ty->mapTypeOutOfContext()); - - if (auto *projection = wrapperInfo.projectionVar) { - auto typeInfo = param->getAttachedPropertyWrapperTypeInfo(0); - auto projectionType = ty->getTypeOfMember(param->getModuleContext(), - typeInfo.projectedValueVar); - projection->setInterfaceType(projectionType->mapTypeOutOfContext()); - } - - ty = computeWrappedValueType(param, ty); - param->getPropertyWrapperWrappedValueVar()->setInterfaceType(ty); - } - param->setInterfaceType(ty->mapTypeOutOfContext()); } }; @@ -1722,6 +1707,4 @@ void TypeChecker::coerceParameterListToType(ParameterList *P, ClosureExpr *CE, params[i].isInOut()); assert(!param->isDefaultArgument() && "Closures cannot have default args"); } - - TypeChecker::checkParameterList(P, CE); } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 902851a37fbbe..7d8e2d0e3b3c9 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -690,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, From 6a8232e184b8333bd7ee7a268ac1bfdfcb005468 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 26 Jan 2021 16:06:37 -0800 Subject: [PATCH 16/38] [PreCheckExpr] Strip $ prefixes out from compound decl names for unqualified lookup in TypeChecker::resolveDeclRefExpr. --- include/swift/AST/Identifier.h | 8 ++++++++ lib/Sema/PreCheckExpr.cpp | 25 ++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/include/swift/AST/Identifier.h b/include/swift/AST/Identifier.h index 71cce7a74fd33..071b022b24433 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("$"); + } 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/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(); From 148635221e776d31612255c36697e59b406b7af4 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 26 Jan 2021 16:28:58 -0800 Subject: [PATCH 17/38] [Property Wrappers] Don't include the $ prefix in synthesized property wrapper var names when the original wrapped property represents a projected-value closure parameter. --- lib/Sema/TypeCheckStorage.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 2b73e7a410166..fc8e1a687d127 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -2439,8 +2439,12 @@ 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. @@ -2695,7 +2699,10 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator, 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); auto dc = var->getDeclContext(); From 140cbaa744d5201e77ec7d2cba05a8ace154f67b Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 26 Jan 2021 17:57:03 -0800 Subject: [PATCH 18/38] [Parser] Allow $ prefixes on argument labels and closure parameter declarations. --- include/swift/Parse/Parser.h | 5 +++-- lib/Parse/ParseExpr.cpp | 6 +++--- lib/Parse/ParsePattern.cpp | 6 ++++-- lib/Parse/ParseType.cpp | 6 ++++-- 4 files changed, 14 insertions(+), 9 deletions(-) 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/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)) { From a4e39688f02a65e27a2f56208bdacf248d1c7cd3 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 26 Jan 2021 18:10:30 -0800 Subject: [PATCH 19/38] [Property Wrappers] Don't allow property wrappers applied to parameters to have a mutating wrappedValue getter. --- include/swift/AST/DiagnosticsSema.def | 3 ++ lib/Sema/TypeCheckStorage.cpp | 5 ++++ .../property_wrapper_parameter_invalid.swift | 29 +++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 test/Sema/property_wrapper_parameter_invalid.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index ae1d83b8ed0d3..a42f204212538 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5490,6 +5490,9 @@ ERROR(property_wrapper_param_arg, none, 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_mutating,none, + "property wrapper applied to parameter must have a nonmutating " + "'wrappedValue' getter", ()) ERROR(property_wrapper_mutating_get_composed_to_get_only,none, "property wrapper %0 with a mutating getter cannot be composed inside " diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index fc8e1a687d127..8115726b2dde3 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -2860,6 +2860,11 @@ PropertyWrapperWrappedValueVarRequest::evaluate(Evaluator &evaluator, localVar->overwriteAccess(var->getFormalAccess()); 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 { diff --git a/test/Sema/property_wrapper_parameter_invalid.swift b/test/Sema/property_wrapper_parameter_invalid.swift new file mode 100644 index 0000000000000..10ff3662cd70a --- /dev/null +++ b/test/Sema/property_wrapper_parameter_invalid.swift @@ -0,0 +1,29 @@ +// 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) {} From ea3fe03c98520c2cbc8ad27fcbd2ed21e6a75ae2 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 27 Jan 2021 16:04:08 -0800 Subject: [PATCH 20/38] [Property Wrappers] Add a constraint fix and diagnostic for the case where the programmer tries to pass a projection argument to a wrapped parameter without 'var projectedValue'. --- include/swift/Sema/CSFix.h | 22 +++++++++++++++++++ include/swift/Sema/ConstraintSystem.h | 2 +- lib/Sema/CSDiagnostics.cpp | 5 +++++ lib/Sema/CSDiagnostics.h | 11 ++++++++++ lib/Sema/CSFix.cpp | 10 +++++++++ lib/Sema/CSGen.cpp | 17 +++++++++++++- lib/Sema/CSSimplify.cpp | 7 ++++-- .../property_wrapper_parameter_invalid.swift | 14 ++++++++++++ 8 files changed, 84 insertions(+), 4 deletions(-) diff --git a/include/swift/Sema/CSFix.h b/include/swift/Sema/CSFix.h index be5a9476080ba..f1ead0e439bd8 100644 --- a/include/swift/Sema/CSFix.h +++ b/include/swift/Sema/CSFix.h @@ -119,6 +119,10 @@ 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, + /// Instead of spelling out `subscript` directly, use subscript operator. UseSubscriptOperator, @@ -957,6 +961,24 @@ class UseWrappedValue final : public ConstraintFix { ConstraintLocator *locator); }; +class AddProjectedValue final : public ConstraintFix { + Type wrapperType; + + AddProjectedValue(ConstraintSystem &cs, Type wrapper, + ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::UseWrappedValue, 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 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 0e4e8d003d339..73dc0a4a5585a 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -4644,7 +4644,7 @@ class ConstraintSystem { /// Matches a wrapped or projected value parameter type to its backing /// property wrapper type by applying the property wrapper. - void applyPropertyWrapperParameter( + TypeMatchResult applyPropertyWrapperParameter( Type wrapperType, Type paramType, ParamDecl *param, Identifier argLabel, ConstraintKind matchKind, ConstraintLocatorBuilder locator); diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 06ccaf96e3c64..b54883633677f 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -3297,6 +3297,11 @@ bool MissingPropertyWrapperUnwrapFailure::diagnoseAsError() { return true; } +bool MissingProjectedValueFailure::diagnoseAsError() { + emitDiagnostic(diag::property_wrapper_param_no_projection, wrapperType); + 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..90862ab0b478f 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1068,6 +1068,17 @@ 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 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..be5d63f63a87f 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -629,6 +629,16 @@ 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 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 df47cf47a5361..be93a41945ec4 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -4059,7 +4059,8 @@ bool ConstraintSystem::generateConstraints( return false; } -void ConstraintSystem::applyPropertyWrapperParameter( +ConstraintSystem::TypeMatchResult +ConstraintSystem::applyPropertyWrapperParameter( Type wrapperType, Type paramType, ParamDecl *param, Identifier argLabel, ConstraintKind matchKind, ConstraintLocatorBuilder locator) { Expr *anchor = getAsExpr(locator.getAnchor()); @@ -4070,6 +4071,19 @@ void ConstraintSystem::applyPropertyWrapperParameter( PropertyWrapperInitKind initKind; if (argLabel.hasDollarPrefix()) { auto typeInfo = param->getAttachedPropertyWrapperTypeInfo(0); + if (!typeInfo.projectedValueVar) { + if (shouldAttemptFixes()) { + if (paramType->hasTypeVariable()) + recordPotentialHole(paramType); + + auto *fix = AddProjectedValue::create(*this, wrapperType, getConstraintLocator(locator)); + if (!recordFix(fix)) + return getTypeMatchSuccess(); + } + + return getTypeMatchFailure(locator); + } + auto projectionType = wrapperType->getTypeOfMember(param->getModuleContext(), typeInfo.projectedValueVar); addConstraint(matchKind, paramType, projectionType, locator); @@ -4089,6 +4103,7 @@ void ConstraintSystem::applyPropertyWrapperParameter( generateConstraints(init, DC); appliedPropertyWrappers[anchor].push_back({ init }); + return getTypeMatchSuccess(); } void ConstraintSystem::optimizeConstraints(Expr *e) { diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 31cf01ec350c0..4bdc024cafb23 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -1422,8 +1422,10 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( if (auto *param = paramInfo.getPropertyWrapperParam(argIdx)) { auto argLabel = argInfo->Labels[argIdx]; - cs.applyPropertyWrapperParameter(paramTy, argTy, const_cast(param), - argLabel, subKind, locator); + if (cs.applyPropertyWrapperParameter(paramTy, argTy, const_cast(param), + argLabel, subKind, locator).isFailure()) { + return cs.getTypeMatchFailure(loc); + } continue; } @@ -10662,6 +10664,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( case FixKind::SkipUnhandledConstructInResultBuilder: case FixKind::UsePropertyWrapper: case FixKind::UseWrappedValue: + case FixKind::AddProjectedValue: case FixKind::ExpandArrayIntoVarargs: case FixKind::UseRawValue: case FixKind::ExplicitlyConstructRawRepresentable: diff --git a/test/Sema/property_wrapper_parameter_invalid.swift b/test/Sema/property_wrapper_parameter_invalid.swift index 10ff3662cd70a..ee0edacafbdcc 100644 --- a/test/Sema/property_wrapper_parameter_invalid.swift +++ b/test/Sema/property_wrapper_parameter_invalid.swift @@ -27,3 +27,17 @@ 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) +} From 12161fa116061960933500ef1af370eb14d80d2f Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 28 Jan 2021 12:41:38 -0800 Subject: [PATCH 21/38] [Sema] Diagnose invalid $ prefixes on closure parameters after the closure is resolved. This can't be diagnosed earlier because a property wrapper attribute may be inferred in the constraint system. --- include/swift/AST/Identifier.h | 2 +- lib/Sema/TypeCheckStmt.cpp | 8 ++++++++ test/Parse/dollar_identifier.swift | 5 ++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/include/swift/AST/Identifier.h b/include/swift/AST/Identifier.h index 071b022b24433..5d93f225c2d96 100644 --- a/include/swift/AST/Identifier.h +++ b/include/swift/AST/Identifier.h @@ -166,7 +166,7 @@ class Identifier { } bool hasDollarPrefix() const { - return str().startswith("$"); + return str().startswith("$") && !(getLength() == 1); } const void *getAsOpaquePointer() const { diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 96c0cbb8f090c..8180e7584b4a8 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" @@ -2080,6 +2081,13 @@ bool TypeChecker::typeCheckClosureBody(ClosureExpr *closure) { TypeChecker::checkClosureAttributes(closure); TypeChecker::checkParameterList(closure->getParameters(), closure); + for (auto *param : *closure->getParameters()) { + if (!param->isImplicit() && param->getName().hasDollarPrefix() && + !param->hasAttachedPropertyWrapper()) { + param->diagnose(diag::dollar_identifier_decl, param->getName()); + } + } + BraceStmt *body = closure->getBody(); Optional timer; diff --git a/test/Parse/dollar_identifier.swift b/test/Parse/dollar_identifier.swift index 287ca04f951bc..62ff491e893dc 100644 --- a/test/Parse/dollar_identifier.swift +++ b/test/Parse/dollar_identifier.swift @@ -77,9 +77,12 @@ 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 // diagnosed after type checking the closure $capture } + let _: (Int) -> Void = { + $a in // expected-error{{cannot declare entity named '$a'}} + } let ($a: _, _) = (0, 0) // expected-error{{cannot declare entity named '$a'}} $label: if true { // expected-error{{cannot declare entity named '$label'}} break $label From 522eedcfd1f6ad81486cd8a726d15297c5b5de06 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 1 Feb 2021 12:52:46 -0800 Subject: [PATCH 22/38] [ConstraintSystem] Emit an error message when trying to pass a property wrapper projection argument to a wrapped parameter with arguments in the wrapper attribute. --- include/swift/AST/DiagnosticsSema.def | 6 ++--- lib/Sema/CSDiagnostics.cpp | 7 +++++ lib/Sema/CSGen.cpp | 17 +++++++++--- lib/Sema/TypeCheckAttr.cpp | 6 ----- .../property_wrapper_parameter_invalid.swift | 27 +++++++++++++++++++ 5 files changed, 51 insertions(+), 12 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index a42f204212538..132c9465fafb2 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5484,12 +5484,12 @@ ERROR(property_wrapper_attribute_not_on_property, none, (Identifier)) NOTE(property_wrapper_declared_here,none, "property wrapper type %0 declared here", (Identifier)) -ERROR(property_wrapper_param_arg, none, - "property wrapper attributes applied to parameters cannot have arguments", - ()) 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_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", ()) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index b54883633677f..0b72688bb101b 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -3291,6 +3291,13 @@ 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); diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index be93a41945ec4..5659a8db81796 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -4070,23 +4070,34 @@ ConstraintSystem::applyPropertyWrapperParameter( PropertyWrapperInitKind initKind; if (argLabel.hasDollarPrefix()) { - auto typeInfo = param->getAttachedPropertyWrapperTypeInfo(0); - if (!typeInfo.projectedValueVar) { + auto attemptProjectedValueFix = [&](ConstraintFix *fix) -> ConstraintSystem::TypeMatchResult { if (shouldAttemptFixes()) { if (paramType->hasTypeVariable()) recordPotentialHole(paramType); - auto *fix = AddProjectedValue::create(*this, wrapperType, getConstraintLocator(locator)); if (!recordFix(fix)) return getTypeMatchSuccess(); } return getTypeMatchFailure(locator); + }; + + auto typeInfo = param->getAttachedPropertyWrapperTypeInfo(0); + if (!typeInfo.projectedValueVar) { + auto *fix = AddProjectedValue::create(*this, wrapperType, getConstraintLocator(locator)); + return attemptProjectedValueFix(fix); } auto projectionType = wrapperType->getTypeOfMember(param->getModuleContext(), typeInfo.projectedValueVar); addConstraint(matchKind, paramType, projectionType, locator); + + if (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); diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 24821417f0500..1b333188472a2 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2995,12 +2995,6 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { return; } - if (isa(D) && attr->getArg()) { - diagnose(attr->getArg()->getLoc(), diag::property_wrapper_param_arg); - attr->setInvalid(); - return; - } - return; } diff --git a/test/Sema/property_wrapper_parameter_invalid.swift b/test/Sema/property_wrapper_parameter_invalid.swift index ee0edacafbdcc..043eb86f85127 100644 --- a/test/Sema/property_wrapper_parameter_invalid.swift +++ b/test/Sema/property_wrapper_parameter_invalid.swift @@ -41,3 +41,30 @@ func testNoProjection(message: String) { // 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) +} From 36008a1d237c80c9eb8b6bc60bcecf88466bd4b7 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Sun, 7 Feb 2021 10:06:31 -0800 Subject: [PATCH 23/38] [NFC][Property Wrappers] Start to add Sema tests for SE-0293. --- test/Sema/property_wrapper_parameter.swift | 87 ++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 test/Sema/property_wrapper_parameter.swift diff --git a/test/Sema/property_wrapper_parameter.swift b/test/Sema/property_wrapper_parameter.swift new file mode 100644 index 0000000000000..a6d54dab8c3d4 --- /dev/null +++ b/test/Sema/property_wrapper_parameter.swift @@ -0,0 +1,87 @@ +// 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() { + let _: (Int) -> Wrapper = { (@Wrapper value) in + _value + } + + let _: (Projection) -> Wrapper = { (@Wrapper $value) in + _value + } +} From 2c2d671dfaefbf37737af9b4c2f8b1717b58884f Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Sun, 7 Feb 2021 11:19:05 -0800 Subject: [PATCH 24/38] [Property Wrappers] Add an unsupported error message for property wrapper parameters in subscript declarations. --- include/swift/AST/DiagnosticsSema.def | 3 +++ lib/Sema/TypeCheckAttr.cpp | 12 ++++++++++++ test/Sema/property_wrapper_parameter_invalid.swift | 5 +++++ 3 files changed, 20 insertions(+) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 132c9465fafb2..c193676756a9b 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5482,6 +5482,9 @@ 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, diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 1b333188472a2..f8169124bd785 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -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/test/Sema/property_wrapper_parameter_invalid.swift b/test/Sema/property_wrapper_parameter_invalid.swift index 043eb86f85127..efbfe0dddb57c 100644 --- a/test/Sema/property_wrapper_parameter_invalid.swift +++ b/test/Sema/property_wrapper_parameter_invalid.swift @@ -68,3 +68,8 @@ func testWrapperAttributeArg(projection: Projection) { // 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 } +} From 96f43153ee4cc9ff97ff716d4e6981eaa1ff13a7 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 8 Feb 2021 23:01:52 -0800 Subject: [PATCH 25/38] [Property Wrappers] Implement support for parameters with an implicit property wrapper in the property wrapper requests. --- include/swift/AST/Decl.h | 5 +- lib/AST/Decl.cpp | 51 +++++++++++++------ lib/AST/TypeCheckRequests.cpp | 8 +-- lib/Sema/TypeCheckPropertyWrapper.cpp | 60 +++++++++++++++++------ lib/Sema/TypeCheckStorage.cpp | 70 ++++++++++++++++++--------- lib/Sema/TypeChecker.h | 4 ++ 6 files changed, 139 insertions(+), 59 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 7eb8a259941d8..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; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index cc94b898362d5..d20475ca1c053 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(); @@ -5966,7 +5983,7 @@ void VarDecl::visitAuxiliaryDecls(llvm::function_ref visit) con visit(backingVar); } - if (getAttrs().hasAttribute()) { + if (getAttrs().hasAttribute() || hasImplicitPropertyWrapper()) { if (auto *backingVar = getPropertyWrapperBackingProperty()) visit(backingVar); @@ -6251,15 +6268,17 @@ 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); - if (auto wrapperType = getPropertyWrapperBackingPropertyType()) - type = wrapperType; - auto label = getArgumentName(); auto flags = ParameterTypeFlags::fromParameterType( type, isVariadic(), isAutoClosure(), isNonEphemeral(), diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 7f7b1e781641e..dc2ee46291020 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -490,22 +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(); + 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/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index 0e9171af283d9..9c33ad5d3551d 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -516,6 +516,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()); @@ -558,15 +561,19 @@ PropertyWrapperBackingPropertyTypeRequest::evaluate( 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; @@ -581,6 +588,19 @@ Type swift::computeWrappedValueType(const VarDecl *var, Type backingStorageType, return wrappedValueType; } +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, @@ -591,6 +611,18 @@ Expr *swift::buildPropertyWrapperInitCall( 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) @@ -613,20 +645,16 @@ Expr *swift::buildPropertyWrapperInitCall( auto attr = wrapperAttrs[i]; if (!attr->getArg()) { Identifier argName; - if (initKind == PropertyWrapperInitKind::ProjectedValue) { - argName = ctx.Id_projectedValue; - } else { - assert(initKind == PropertyWrapperInitKind::WrappedValue); - switch (var->getAttachedPropertyWrapperTypeInfo(i).wrappedValueInit) { - case PropertyWrapperTypeInfo::HasInitialValueInit: - argName = ctx.Id_initialValue; - break; + assert(initKind == PropertyWrapperInitKind::WrappedValue); + switch (var->getAttachedPropertyWrapperTypeInfo(i).wrappedValueInit) { + case PropertyWrapperTypeInfo::HasInitialValueInit: + argName = ctx.Id_initialValue; + break; - case PropertyWrapperTypeInfo::HasWrappedValueInit: - case PropertyWrapperTypeInfo::NoWrappedValueInit: - argName = ctx.Id_wrappedValue; - break; - } + case PropertyWrapperTypeInfo::HasWrappedValueInit: + case PropertyWrapperTypeInfo::NoWrappedValueInit: + argName = ctx.Id_wrappedValue; + break; } auto endLoc = initializer->getEndLoc(); diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 8115726b2dde3..242670ae89d24 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -2482,12 +2482,18 @@ 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()); + } if (!isa(var)) var->getAttrs().add( @@ -2691,10 +2697,11 @@ PropertyWrapperLValuenessRequest::evaluate(Evaluator &, PropertyWrapperBackingPropertyInfo PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator, VarDecl *var) const { - auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(0); - if (!wrapperInfo) + if (!var->hasAttachedPropertyWrapper()) return PropertyWrapperBackingPropertyInfo(); + auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(0); + // Compute the name of the storage type. ASTContext &ctx = var->getASTContext(); SmallString<64> nameBuf; @@ -2723,14 +2730,18 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator, } VarDecl *projectionVar = nullptr; - if (wrapperInfo.projectedValueVar) { - projectionVar = synthesizePropertyWrapperProjectionVar(ctx, var, wrapperType, - wrapperInfo.projectedValueVar); + if (wrapperInfo.projectedValueVar || var->getName().hasDollarPrefix()) { + projectionVar = + synthesizePropertyWrapperProjectionVar(ctx, var, wrapperType, + wrapperInfo.projectedValueVar); } return PropertyWrapperBackingPropertyInfo(backingVar, projectionVar, nullptr, nullptr); } + if (!wrapperInfo) + return PropertyWrapperBackingPropertyInfo(); + // Determine the type of the storage. auto wrapperType = var->getPropertyWrapperBackingPropertyType(); if (!wrapperType || wrapperType->hasError()) @@ -2850,25 +2861,40 @@ PropertyWrapperWrappedValueVarRequest::evaluate(Evaluator &evaluator, 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(), - var->getName(), dc); - localVar->setInterfaceType(var->getInterfaceType()); + var->getLoc(), name, dc); + if (!var->hasImplicitPropertyWrapper()) + localVar->setInterfaceType(var->getInterfaceType()); localVar->setImplicit(); localVar->getAttrs() = var->getAttrs(); localVar->overwriteAccess(var->getFormalAccess()); - 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 { + 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()); + } } evaluator.cacheOutput(PropertyWrapperBackingPropertyInfoRequest{localVar}, diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 7d8e2d0e3b3c9..d979ce4b76113 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -1248,6 +1248,10 @@ bool isValidKeyPathDynamicMemberLookup(SubscriptDecl *decl, Type computeWrappedValueType(const VarDecl *var, Type backingStorageType, Optional limit = None); +/// 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. From 133dec0e05d4cdc0e206c3530d78ad516444c02e Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 8 Feb 2021 23:25:11 -0800 Subject: [PATCH 26/38] [ConstraintSystem] Implement implicit property wrapper attribute inference for closure parameters. --- include/swift/AST/DiagnosticsSema.def | 3 ++ include/swift/Sema/CSFix.h | 23 +++++++++- lib/Sema/CSApply.cpp | 24 +++++------ lib/Sema/CSDiagnostics.cpp | 9 ++++ lib/Sema/CSDiagnostics.h | 11 +++++ lib/Sema/CSFix.cpp | 11 +++++ lib/Sema/CSGen.cpp | 20 ++------- lib/Sema/CSSimplify.cpp | 49 ++++++++++++++++++++++ lib/Sema/TypeCheckStmt.cpp | 7 ---- test/Parse/dollar_identifier.swift | 5 +-- test/Sema/property_wrapper_parameter.swift | 31 ++++++++++++-- 11 files changed, 149 insertions(+), 44 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index c193676756a9b..3da801210a8d2 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5496,6 +5496,9 @@ ERROR(property_wrapper_param_projection_invalid,none, 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 " diff --git a/include/swift/Sema/CSFix.h b/include/swift/Sema/CSFix.h index f1ead0e439bd8..8672f1992bf77 100644 --- a/include/swift/Sema/CSFix.h +++ b/include/swift/Sema/CSFix.h @@ -123,6 +123,9 @@ enum class FixKind : uint8_t { /// a projection argument. AddProjectedValue, + /// Add '@propertyWrapper' to a nominal type declaration. + AddPropertyWrapperAttribute, + /// Instead of spelling out `subscript` directly, use subscript operator. UseSubscriptOperator, @@ -966,7 +969,7 @@ class AddProjectedValue final : public ConstraintFix { AddProjectedValue(ConstraintSystem &cs, Type wrapper, ConstraintLocator *locator) - : ConstraintFix(cs, FixKind::UseWrappedValue, locator), wrapperType(wrapper) {} + : ConstraintFix(cs, FixKind::AddProjectedValue, locator), wrapperType(wrapper) {} public: static AddProjectedValue *create(ConstraintSystem &cs, Type wrapper, @@ -979,6 +982,24 @@ class AddProjectedValue final : public ConstraintFix { 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/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index aaa93f96f3a0d..17c8d71630a30 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7831,23 +7831,23 @@ namespace { if (!param->hasAttachedPropertyWrapper()) continue; - auto wrapperInfo = param->getPropertyWrapperBackingPropertyInfo(); - auto *backingVar = wrapperInfo.backingVar; - auto wrapperType = solution.simplifyType(solution.getType(backingVar)); - backingVar->setInterfaceType(wrapperType->mapTypeOutOfContext()); - - if (auto *projection = wrapperInfo.projectionVar) { - auto typeInfo = param->getAttachedPropertyWrapperTypeInfo(0); - auto projectionType = wrapperType->getTypeOfMember(param->getModuleContext(), - typeInfo.projectedValueVar); - projection->setInterfaceType(projectionType); + // 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(); - auto wrappedValueType = computeWrappedValueType(param, wrapperType); - wrappedValueVar->setInterfaceType(wrappedValueType); + wrappedValueVar->setInterfaceType( + solution.simplifyType(solution.getType(wrappedValueVar))); } + TypeChecker::checkParameterList(closure->getParameters(), closure); + auto &appliedWrappers = Rewriter.solution.appliedPropertyWrappers[closure]; return Rewriter.buildPropertyWrapperFnThunk(closure, closureFnType, closure, appliedWrappers); diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 0b72688bb101b..77aa6c13c6d2c 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -3309,6 +3309,15 @@ bool MissingProjectedValueFailure::diagnoseAsError() { return true; } +bool MissingPropertyWrapperAttributeFailure::diagnoseAsError() { + 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. + + 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 90862ab0b478f..abbf26ecf5fa7 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1079,6 +1079,17 @@ class MissingProjectedValueFailure final : public FailureDiagnostic { 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 be5d63f63a87f..02ccf27666273 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -639,6 +639,17 @@ AddProjectedValue *AddProjectedValue::create(ConstraintSystem &cs, Type wrapperT 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 5659a8db81796..32ce1f8d450ce 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2053,18 +2053,6 @@ namespace { TVO_CanBindToInOut | TVO_CanBindToNoEscape | TVO_CanBindToHole); } - if (auto wrapperInfo = param->getPropertyWrapperBackingPropertyInfo()) { - auto *wrapperAttr = param->getAttachedPropertyWrappers().front(); - auto wrapperType = param->getAttachedPropertyWrapperType(0); - auto backingType = CS.openUnboundGenericTypes( - wrapperType, CS.getConstraintLocator(wrapperAttr->getTypeRepr())); - CS.setType(wrapperInfo.backingVar, backingType); - - CS.applyPropertyWrapperParameter(backingType, externalType, param, - param->getName(), ConstraintKind::Equal, - CS.getConstraintLocator(closure)); - } - closureParams.push_back(param->toFunctionParam(externalType)); } } @@ -4082,17 +4070,17 @@ ConstraintSystem::applyPropertyWrapperParameter( return getTypeMatchFailure(locator); }; - auto typeInfo = param->getAttachedPropertyWrapperTypeInfo(0); + auto typeInfo = wrapperType->getAnyNominal()->getPropertyWrapperTypeInfo(); if (!typeInfo.projectedValueVar) { auto *fix = AddProjectedValue::create(*this, wrapperType, getConstraintLocator(locator)); return attemptProjectedValueFix(fix); } - auto projectionType = wrapperType->getTypeOfMember(param->getModuleContext(), - typeInfo.projectedValueVar); + Type projectionType = computeProjectedValueType(param, wrapperType); addConstraint(matchKind, paramType, projectionType, locator); - if (param->getAttachedPropertyWrappers().front()->getArg()) { + if (!param->hasImplicitPropertyWrapper() && + param->getAttachedPropertyWrappers().front()->getArg()) { auto *fix = UseWrappedValue::create(*this, param, /*base=*/Type(), wrapperType, getConstraintLocator(locator)); return attemptProjectedValueFix(fix); diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 4bdc024cafb23..f3ca8f36477b0 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -8197,6 +8197,54 @@ 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 = applyPropertyWrapperParameter(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 (paramDecl->getTypeRepr()) { // Internal type is the type used in the body of the closure, @@ -10665,6 +10713,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( 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/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 8180e7584b4a8..3ea939cd33d76 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -2081,13 +2081,6 @@ bool TypeChecker::typeCheckClosureBody(ClosureExpr *closure) { TypeChecker::checkClosureAttributes(closure); TypeChecker::checkParameterList(closure->getParameters(), closure); - for (auto *param : *closure->getParameters()) { - if (!param->isImplicit() && param->getName().hasDollarPrefix() && - !param->hasAttachedPropertyWrapper()) { - param->diagnose(diag::dollar_identifier_decl, param->getName()); - } - } - BraceStmt *body = closure->getBody(); Optional timer; diff --git a/test/Parse/dollar_identifier.swift b/test/Parse/dollar_identifier.swift index 62ff491e893dc..fbb14e21b8fb2 100644 --- a/test/Parse/dollar_identifier.swift +++ b/test/Parse/dollar_identifier.swift @@ -77,12 +77,9 @@ 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 // diagnosed after type checking the closure + $a in // expected-error{{inferred projection type 'Int' is not a property wrapper}} $capture } - let _: (Int) -> Void = { - $a in // expected-error{{cannot declare entity named '$a'}} - } let ($a: _, _) = (0, 0) // expected-error{{cannot declare entity named '$a'}} $label: if true { // expected-error{{cannot declare entity named '$label'}} break $label diff --git a/test/Sema/property_wrapper_parameter.swift b/test/Sema/property_wrapper_parameter.swift index a6d54dab8c3d4..3ba6b13d4d3fc 100644 --- a/test/Sema/property_wrapper_parameter.swift +++ b/test/Sema/property_wrapper_parameter.swift @@ -77,11 +77,34 @@ func testMethods(instance: S, Metatype: S.Type, } func testClosures() { - let _: (Int) -> Wrapper = { (@Wrapper value) in - _value + 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 _: (Projection) -> Wrapper = { (@Wrapper $value) in - _value + let _: (ProjectionWrapper) -> PropertyWrapperTuple = { $value in + (_value, value, $value) } } From e40d7ebe1514021d95c34aa8481b8b7a56d2eb11 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 9 Feb 2021 01:41:04 -0800 Subject: [PATCH 27/38] [NFC][Property Wrappers] Start to add SILGen tests for SE-0293. --- test/SILGen/property_wrapper_parameter.swift | 165 +++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 test/SILGen/property_wrapper_parameter.swift diff --git a/test/SILGen/property_wrapper_parameter.swift b/test/SILGen/property_wrapper_parameter.swift new file mode 100644 index 0000000000000..bb691c07338c4 --- /dev/null +++ b/test/SILGen/property_wrapper_parameter.swift @@ -0,0 +1,165 @@ +// 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 + + // 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_parameter7WrapperV12wrappedValueACyxGx_tcfC : $@convention(method) <τ_0_0> (@in τ_0_0, @thin Wrapper<τ_0_0>.Type) -> @out Wrapper<τ_0_0> + + testSimpleWrapperParameter($value: projection) + // CHECK: function_ref @$s26property_wrapper_parameter7WrapperV14projectedValueACyxGAA10ProjectionVyxG_tcfC : $@convention(method) <τ_0_0> (@in Projection<τ_0_0>, @thin Wrapper<τ_0_0>.Type) -> @out Wrapper<τ_0_0> +} + +// CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_parameter33testSimpleClosureWrapperParameteryyF : $@convention(thin) () -> () +func testSimpleClosureWrapperParameter() { + let closure: (Int) -> Void = { (@Wrapper value) 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) -> () + + // 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!" + + // 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 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) -> () + + // 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 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) + + // 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 +} From aa0da03d2a45ecf885f4590e5b46c8a8b2a6c392 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 15 Feb 2021 19:34:59 -0800 Subject: [PATCH 28/38] [CSGen] Move generateWrappedPropertyTypeConstraints back to its original position in CSGen. This effectively reverts 6caca26014. --- lib/Sema/CSGen.cpp | 99 +++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 32ce1f8d450ce..cd58dda76e6fe 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -777,55 +777,6 @@ namespace { namespace { - /// Generate constraints to produce the wrapped value type given the property - /// that has an attached property wrapper. - /// - /// \param initializerType The type of the adjusted initializer, which - /// initializes the underlying storage variable. - /// \param wrappedVar The property that has a property wrapper. - /// \returns the type of the property. - bool generateWrappedPropertyTypeConstraints( - ConstraintSystem &cs, Type initializerType, VarDecl *wrappedVar, Type propertyType) { - auto dc = wrappedVar->getInnermostDeclContext(); - - Type wrapperType = LValueType::get(initializerType); - Type wrappedValueType; - - auto wrapperAttributes = wrappedVar->getAttachedPropertyWrappers(); - for (unsigned i : indices(wrapperAttributes)) { - // FIXME: We should somehow pass an OpenUnboundGenericTypeFn to - // AttachedPropertyWrapperTypeRequest::evaluate to open up unbound - // generics on the fly. - Type rawWrapperType = wrappedVar->getAttachedPropertyWrapperType(i); - auto wrapperInfo = wrappedVar->getAttachedPropertyWrapperTypeInfo(i); - if (rawWrapperType->hasError() || !wrapperInfo) - return true; - - // The former wrappedValue type must be equal to the current wrapper type - if (wrappedValueType) { - auto *typeRepr = wrapperAttributes[i]->getTypeRepr(); - auto *locator = - cs.getConstraintLocator(typeRepr, LocatorPathElt::ContextualType()); - wrapperType = cs.replaceInferableTypesWithTypeVars(rawWrapperType, - locator); - cs.addConstraint(ConstraintKind::Equal, wrapperType, wrappedValueType, - locator); - cs.setContextualType(typeRepr, TypeLoc::withoutLoc(wrappedValueType), - CTP_ComposedPropertyWrapper); - } - - wrappedValueType = wrapperType->getTypeOfMember( - dc->getParentModule(), wrapperInfo.valueVar); - } - - // The property type must be equal to the wrapped value type - cs.addConstraint(ConstraintKind::Equal, propertyType, wrappedValueType, - cs.getConstraintLocator(wrappedVar, LocatorPathElt::ContextualType())); - cs.setContextualType(wrappedVar, TypeLoc::withoutLoc(wrappedValueType), - CTP_WrappedProperty); - return false; - } - class ConstraintGenerator : public ExprVisitor { ConstraintSystem &CS; DeclContext *CurDC; @@ -3603,6 +3554,56 @@ static Expr *generateConstraintsFor(ConstraintSystem &cs, Expr *expr, return result; } +/// Generate constraints to produce the wrapped value type given the property +/// that has an attached property wrapper. +/// +/// \param initializerType The type of the adjusted initializer, which +/// initializes the underlying storage variable. +/// \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) { + auto dc = wrappedVar->getInnermostDeclContext(); + + Type wrapperType = LValueType::get(initializerType); + Type wrappedValueType; + + auto wrapperAttributes = wrappedVar->getAttachedPropertyWrappers(); + for (unsigned i : indices(wrapperAttributes)) { + // FIXME: We should somehow pass an OpenUnboundGenericTypeFn to + // AttachedPropertyWrapperTypeRequest::evaluate to open up unbound + // generics on the fly. + Type rawWrapperType = wrappedVar->getAttachedPropertyWrapperType(i); + auto wrapperInfo = wrappedVar->getAttachedPropertyWrapperTypeInfo(i); + if (rawWrapperType->hasError() || !wrapperInfo) + return true; + + // The former wrappedValue type must be equal to the current wrapper type + if (wrappedValueType) { + auto *typeRepr = wrapperAttributes[i]->getTypeRepr(); + auto *locator = + cs.getConstraintLocator(typeRepr, LocatorPathElt::ContextualType()); + wrapperType = cs.replaceInferableTypesWithTypeVars(rawWrapperType, + locator); + cs.addConstraint(ConstraintKind::Equal, wrapperType, wrappedValueType, + locator); + cs.setContextualType(typeRepr, TypeLoc::withoutLoc(wrappedValueType), + CTP_ComposedPropertyWrapper); + } + + wrappedValueType = wrapperType->getTypeOfMember( + dc->getParentModule(), wrapperInfo.valueVar); + } + + // The property type must be equal to the wrapped value type + cs.addConstraint(ConstraintKind::Equal, propertyType, wrappedValueType, + cs.getConstraintLocator(wrappedVar, LocatorPathElt::ContextualType())); + cs.setContextualType(wrappedVar, TypeLoc::withoutLoc(wrappedValueType), + CTP_WrappedProperty); + return false; +} + /// Generate additional constraints for the pattern of an initialization. static bool generateInitPatternConstraints( ConstraintSystem &cs, SolutionApplicationTarget target, Expr *initializer) { From 13692fefdeae97d7bdc99f55bebdc61abfbe572e Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Sat, 20 Feb 2021 14:31:14 -0800 Subject: [PATCH 29/38] [Property Wrappers] Store property wrapper "init from projection" expressions in PropertyWrapperBackingPropertyInfo. --- include/swift/AST/PropertyWrappers.h | 85 ++++++++++++++++++++-------- lib/AST/Decl.cpp | 4 +- lib/SILGen/SILGen.cpp | 10 ++-- lib/SILGen/SILGenConstructor.cpp | 6 +- lib/SILGen/SILGenDecl.cpp | 4 +- lib/SILGen/SILGenFunction.cpp | 4 +- lib/SILGen/SILGenLValue.cpp | 6 +- lib/SILGen/SILGenType.cpp | 2 +- lib/Sema/CodeSynthesis.cpp | 2 +- lib/Sema/TypeCheckStorage.cpp | 9 ++- lib/TBDGen/TBDGen.cpp | 2 +- 11 files changed, 85 insertions(+), 49 deletions(-) diff --git a/include/swift/AST/PropertyWrappers.h b/include/swift/AST/PropertyWrappers.h index 53af955e87beb..692c4ae9f6969 100644 --- a/include/swift/AST/PropertyWrappers.h +++ b/include/swift/AST/PropertyWrappers.h @@ -153,6 +153,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. @@ -162,30 +170,67 @@ 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; + } + explicit operator bool() const { return isValid(); } friend bool operator==(const PropertyWrapperBackingPropertyInfo &lhs, @@ -203,14 +248,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/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index d20475ca1c053..22a19e3ebcfa8 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -6034,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(); diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index c09b4ca0a4392..063588d7bbecb 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,12 @@ 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; } 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/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index b28ccf8b2217b..036aa995d7812 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -929,12 +929,12 @@ 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.initializeFromOriginal); + assert(value == wrappedInfo.getInitFromWrappedValue()); } emitReturnExpr(Loc, value); diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 1ac00ec0a9572..eb8286ce98d4a 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -1307,7 +1307,7 @@ namespace { // 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 +1322,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/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/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/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 242670ae89d24..514b583e4f34c 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -2736,7 +2736,7 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator, wrapperInfo.projectedValueVar); } - return PropertyWrapperBackingPropertyInfo(backingVar, projectionVar, nullptr, nullptr); + return PropertyWrapperBackingPropertyInfo(backingVar, projectionVar); } if (!wrapperInfo) @@ -2834,8 +2834,7 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator, // value. if (!wrappedValue && (!var->allAttachedPropertyWrappersHaveWrappedValueInit() || initializer)) { - return PropertyWrapperBackingPropertyInfo( - backingVar, storageVar, nullptr, nullptr); + return PropertyWrapperBackingPropertyInfo(backingVar, storageVar); } // Form the initialization of the backing property from a value of the @@ -2845,11 +2844,11 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator, ctx, var->getSourceRange(), var->getType(), /*wrappedValue=*/nullptr); typeCheckSynthesizedWrapperInitializer( pbd, backingVar, parentPBD, initializer); - wrappedValue = findWrappedValuePlaceholder(initializer); } return PropertyWrapperBackingPropertyInfo(backingVar, storageVar, - initializer, wrappedValue); + /*wrappedValueInitExpr=*/initializer, + /*projectedValueInitExpr=*/nullptr); } VarDecl * 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)); } From 648c5753dfde10b724b269c325d9fb29ccc841cc Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 24 Feb 2021 19:36:01 -0800 Subject: [PATCH 30/38] [SILGen] Teach SILGen to emit property wrapper generator functions that take in a projected value. --- docs/ABI/Mangling.rst | 1 + include/swift/AST/ASTMangler.h | 3 +++ include/swift/AST/PropertyWrappers.h | 16 +++++++++++ include/swift/Demangling/DemangleNodes.def | 1 + include/swift/SIL/SILDeclRef.h | 7 ++++- lib/AST/ASTMangler.cpp | 13 +++++++++ lib/Demangling/Demangler.cpp | 5 ++++ lib/Demangling/NodePrinter.cpp | 8 +++++- lib/Demangling/OldRemangler.cpp | 5 ++++ lib/Demangling/Remangler.cpp | 8 ++++++ lib/IRGen/GenObjC.cpp | 1 + lib/SIL/IR/SILDeclRef.cpp | 4 +++ lib/SIL/IR/SILFunctionType.cpp | 4 +++ lib/SIL/IR/SILPrinter.cpp | 3 +++ lib/SIL/IR/TypeLowering.cpp | 27 +++++++++++++++++++ lib/SIL/Parser/ParseSIL.cpp | 3 +++ lib/SILGen/SILGen.cpp | 31 ++++++++++++++++++++-- lib/SILGen/SILGenApply.cpp | 6 ++++- lib/SILGen/SILGenFunction.cpp | 29 +++++++++++++++++--- lib/SILGen/SILGenFunction.h | 1 + lib/SILGen/SILGenProlog.cpp | 10 +++++-- 21 files changed, 176 insertions(+), 10 deletions(-) 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/PropertyWrappers.h b/include/swift/AST/PropertyWrappers.h index 692c4ae9f6969..fc4be80a0f107 100644 --- a/include/swift/AST/PropertyWrappers.h +++ b/include/swift/AST/PropertyWrappers.h @@ -231,6 +231,22 @@ struct PropertyWrapperBackingPropertyInfo { 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, 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/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/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 4c63128870616..791a5b1b69049 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); @@ -2806,6 +2814,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/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/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/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index 57f17157a811b..d9860e58f4b9d 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -838,6 +838,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 063588d7bbecb..65153fe2f3f0d 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -944,6 +944,24 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *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; + } + case SILDeclRef::Kind::GlobalAccessor: { auto *global = cast(constant.getDecl()); auto found = delayedGlobals.find(global); @@ -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/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 036aa995d7812..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: @@ -896,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"), @@ -906,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()); } @@ -935,6 +948,16 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, Expr *value, 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.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/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index 9e9412c9ca052..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,8 +243,13 @@ struct ArgumentInitHelper { } void emitParam(ParamDecl *PD) { - if (auto *backingVar = PD->getPropertyWrapperBackingProperty()) - PD = cast(backingVar); + if (auto wrapperInfo = PD->getPropertyWrapperBackingPropertyInfo()) { + if (wrapperInfo.hasSynthesizedInitializers()) { + SGF.SGM.emitPropertyWrapperBackingInitializer(PD); + } + + PD = cast(wrapperInfo.backingVar); + } auto type = PD->getType(); From b821c8d76bfe9e4e9db763424ce74dbfa3d52727 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 24 Feb 2021 19:43:46 -0800 Subject: [PATCH 31/38] [Sema] Add an implicit applied property wrapper expression and a new dedicated initializer context for this expressions. --- include/swift/AST/DiagnosticsSema.def | 10 +++--- include/swift/AST/Expr.h | 51 +++++++++++++++++++++++++++ include/swift/AST/ExprNodes.def | 1 + include/swift/AST/Initializer.h | 40 +++++++++++++++++++-- lib/AST/ASTDumper.cpp | 9 +++++ lib/AST/ASTMangler.cpp | 13 +++++++ lib/AST/ASTWalker.cpp | 10 ++++++ lib/AST/DeclContext.cpp | 13 +++++++ lib/AST/Expr.cpp | 9 +++++ lib/IDE/ExprContextAnalysis.cpp | 9 +++++ lib/SILGen/SILGenExpr.cpp | 21 +++++++++++ lib/Sema/CSApply.cpp | 4 +++ lib/Sema/CSGen.cpp | 4 +++ lib/Sema/TypeCheckEffects.cpp | 10 ++++++ 14 files changed, 197 insertions(+), 7 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 3da801210a8d2..e32b8ad7b3ce2 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, diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index ba622dc2dcb58..a292950f2f8bf 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -4228,6 +4228,57 @@ class PropertyWrapperValuePlaceholderExpr : public Expr { } }; +/// An implicit applied property wrapper expression. +class AppliedPropertyWrapperExpr final : public Expr { +public: + enum class ValueKind { + WrappedValue, + ProjectedValue + }; + +private: + /// 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(const ParamDecl *param, SourceLoc loc, + Type Ty, Expr *value, ValueKind kind) + : Expr(ExprKind::AppliedPropertyWrapper, /*Implicit=*/true, Ty), + Param(param), Loc(loc), Value(value), Kind(kind) {} + +public: + static AppliedPropertyWrapperExpr * + create(ASTContext &ctx, const ParamDecl *param, SourceLoc loc, + Type Ty, Expr *value, ValueKind kind); + + SourceRange getSourceRange() const { return Loc; } + + /// 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/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/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 791a5b1b69049..5dc8af14975c5 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -2116,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"); diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index defe38e63b5c2..21dc9a77e674a 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -514,6 +514,16 @@ class Traversal : public ASTVisitorgetValue())) { + E->setValue(newValue); + } else { + return nullptr; + } + + return E; + } + Expr *visitDefaultArgumentExpr(DefaultArgumentExpr *E) { return E; } Expr *visitInterpolatedStringLiteralExpr(InterpolatedStringLiteralExpr *E) { 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 0fde5141de313..912b48a2d6574 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: @@ -1507,6 +1509,13 @@ PropertyWrapperValuePlaceholderExpr::create(ASTContext &ctx, SourceRange range, range, ty, placeholder, wrappedValue, isAutoClosure); } +AppliedPropertyWrapperExpr * +AppliedPropertyWrapperExpr::create(ASTContext &ctx, const ParamDecl *param, + SourceLoc loc, Type Ty, Expr *value, + AppliedPropertyWrapperExpr::ValueKind kind) { + return new (ctx) AppliedPropertyWrapperExpr(param, loc, Ty, value, kind); +} + const ParamDecl *DefaultArgumentExpr::getParamDecl() const { return getParameterAt(DefaultArgsOwner.getDecl(), ParamIndex); } 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/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 29ca7a958ff84..2cd1f6ece8d40 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,25 @@ 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; + } + + return SGF.emitApplyOfPropertyWrapperBackingInitializer( + SILLocation(E), param, SGF.getForwardingSubstitutionMap(), + std::move(argument), initKind); +} + ProtocolDecl *SILGenFunction::getPointerProtocol() { if (SGM.PointerProtocol) return *SGM.PointerProtocol; diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 17c8d71630a30..0959af15e2f89 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -3573,6 +3573,10 @@ namespace { return expr; } + Expr *visitAppliedPropertyWrapperExpr(AppliedPropertyWrapperExpr *expr) { + llvm_unreachable("Already type-checked"); + } + Expr *visitDefaultArgumentExpr(DefaultArgumentExpr *expr) { llvm_unreachable("Already type-checked"); } diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index cd58dda76e6fe..8ed0c6631baa8 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2614,6 +2614,10 @@ namespace { return CS.getType(expr); } + Type visitAppliedPropertyWrapperExpr(AppliedPropertyWrapperExpr *expr) { + return expr->getType(); + } + Type visitDefaultArgumentExpr(DefaultArgumentExpr *expr) { return expr->getType(); } 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: From 9cdecf4703b5db45cf3351c6d2ef4ef9e0823ca7 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 24 Feb 2021 19:47:57 -0800 Subject: [PATCH 32/38] [Property Wrappers] Compute generic wrapped-value and projected-value initialization expressions for parameters with attached property wrappers. --- include/swift/AST/PropertyWrappers.h | 2 + lib/SILGen/SILGenLValue.cpp | 3 + lib/Sema/TypeCheckPropertyWrapper.cpp | 8 +- lib/Sema/TypeCheckStorage.cpp | 186 +++++++++++++++----------- 4 files changed, 123 insertions(+), 76 deletions(-) diff --git a/include/swift/AST/PropertyWrappers.h b/include/swift/AST/PropertyWrappers.h index fc4be80a0f107..21e5898cf2e8b 100644 --- a/include/swift/AST/PropertyWrappers.h +++ b/include/swift/AST/PropertyWrappers.h @@ -63,6 +63,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` diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index eb8286ce98d4a..fe69b3d6032e9 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -1304,6 +1304,9 @@ 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(); diff --git a/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index 9c33ad5d3551d..2a4384a55c48f 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -339,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 = @@ -527,7 +533,7 @@ PropertyWrapperBackingPropertyTypeRequest::evaluate( return Type(); // The constraint system will infer closure parameter types - if (isa(var) && !var->getDeclContext()->getAsDecl()) + if (isa(var) && var->getInterfaceType()->hasError()) return Type(); // If there's an initializer of some sort, checking it will determine the diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 514b583e4f34c..7b9c4c56e83bd 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -2713,15 +2713,18 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator, Identifier name = ctx.getIdentifier(nameBuf); auto dc = var->getDeclContext(); + VarDecl *backingVar = nullptr; + VarDecl *projectionVar = nullptr; + if (auto *param = dyn_cast(var)) { - auto *backingVar = ParamDecl::cloneWithoutType(ctx, param); + 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 (dc->getAsDecl()) { + if (!var->getInterfaceType()->hasError()) { wrapperType = var->getPropertyWrapperBackingPropertyType(); if (!wrapperType || wrapperType->hasError()) return PropertyWrapperBackingPropertyInfo(); @@ -2729,101 +2732,120 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator, backingVar->setInterfaceType(wrapperType); } - VarDecl *projectionVar = nullptr; if (wrapperInfo.projectedValueVar || var->getName().hasDollarPrefix()) { projectionVar = synthesizePropertyWrapperProjectionVar(ctx, var, wrapperType, wrapperInfo.projectedValueVar); } - - return PropertyWrapperBackingPropertyInfo(backingVar, projectionVar); } if (!wrapperInfo) - return PropertyWrapperBackingPropertyInfo(); + return PropertyWrapperBackingPropertyInfo(backingVar, projectionVar); // Determine the type of the storage. auto wrapperType = var->getPropertyWrapperBackingPropertyType(); if (!wrapperType || wrapperType->hasError()) - return PropertyWrapperBackingPropertyInfo(); + 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: @@ -2834,21 +2856,35 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator, // value. if (!wrappedValue && (!var->allAttachedPropertyWrappersHaveWrappedValueInit() || initializer)) { - return PropertyWrapperBackingPropertyInfo(backingVar, storageVar); + 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); + + 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, storageVar, - /*wrappedValueInitExpr=*/initializer, - /*projectedValueInitExpr=*/nullptr); + return PropertyWrapperBackingPropertyInfo(backingVar, projectionVar, + wrappedValueInit, + projectedValueInit); } VarDecl * From 99e066683c325c391bc6ac900891e5dc9c90bf41 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 24 Feb 2021 19:49:57 -0800 Subject: [PATCH 33/38] [ConstraintSystem] Inject implicit applied property wrapper expressions around arguments to property wrapper parameters. --- include/swift/AST/PropertyWrappers.h | 7 +++ include/swift/Sema/ConstraintSystem.h | 4 +- lib/Sema/CSApply.cpp | 46 ++++++++++---------- lib/Sema/CSGen.cpp | 11 +---- test/SILGen/property_wrapper_parameter.swift | 31 ++++++++++--- 5 files changed, 58 insertions(+), 41 deletions(-) diff --git a/include/swift/AST/PropertyWrappers.h b/include/swift/AST/PropertyWrappers.h index 21e5898cf2e8b..f34ba02409423 100644 --- a/include/swift/AST/PropertyWrappers.h +++ b/include/swift/AST/PropertyWrappers.h @@ -42,6 +42,13 @@ enum class PropertyWrapperInitKind { 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 diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 73dc0a4a5585a..814f6c14b8b3c 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -1187,7 +1187,7 @@ class Solution { resultBuilderTransformed; /// A map from argument expressions to their applied property wrapper expressions. - llvm::MapVector> appliedPropertyWrappers; + llvm::MapVector> appliedPropertyWrappers; /// Simplify the given type by substituting all occurrences of /// type variables for their fixed types. @@ -2238,7 +2238,7 @@ class ConstraintSystem { public: /// A map from argument expressions to their applied property wrapper expressions. - llvm::SmallMapVector, 4> appliedPropertyWrappers; + llvm::SmallMapVector, 4> appliedPropertyWrappers; /// The locators of \c Defaultable constraints whose defaults were used. std::vector DefaultedConstraints; diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 0959af15e2f89..98f136053e833 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -979,7 +979,7 @@ namespace { AutoClosureExpr *buildPropertyWrapperFnThunk( Expr *fnRef, FunctionType *fnType, AnyFunctionRef fnDecl, - ArrayRef appliedPropertyWrappers) { + ArrayRef appliedPropertyWrappers) { auto &context = cs.getASTContext(); auto paramInfo = fnType->getParams(); @@ -1010,17 +1010,17 @@ namespace { if (auto wrapperInfo = innerParam->getPropertyWrapperBackingPropertyInfo()) { // Rewrite the parameter ref to the backing wrapper initialization // expression. - Expr *init = appliedPropertyWrappers[appliedWrapperIndex++]; - auto *placeholder = findWrappedValuePlaceholder(init); - placeholder->setOriginalWrappedValue(paramRef); - - auto target = SolutionApplicationTarget(init, cs.DC, CTP_Unused, Type(), - /*isDiscarded=*/false); - auto result = cs.applySolution(solution, target); - if (!result) - return nullptr; + 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 = result->getAsExpr(); + paramRef = AppliedPropertyWrapperExpr::create(context, innerParam, + innerParam->getStartLoc(), + wrapperType, paramRef, valueKind); cs.cacheExprTypes(paramRef); // SILGen knows how to emit property-wrapped parameters, but the @@ -1836,7 +1836,7 @@ namespace { ConcreteDeclRef callee, ApplyExpr *apply, ArrayRef argLabels, ConstraintLocatorBuilder locator, - ArrayRef appliedPropertyWrappers); + ArrayRef appliedPropertyWrappers); /// Coerce the given 'self' argument (e.g., for the base of a /// member expression) to the given type. @@ -5599,7 +5599,7 @@ Expr *ExprRewriter::coerceCallArguments( ApplyExpr *apply, ArrayRef argLabels, ConstraintLocatorBuilder locator, - ArrayRef appliedPropertyWrappers) { + ArrayRef appliedPropertyWrappers) { auto &ctx = getConstraintSystem().getASTContext(); auto params = funcType->getParams(); unsigned appliedWrapperIndex = 0; @@ -5832,19 +5832,17 @@ Expr *ExprRewriter::coerceCallArguments( return true; }; - if (paramInfo.getPropertyWrapperParam(paramIdx)) { - Expr *init = appliedPropertyWrappers[appliedWrapperIndex++]; - auto *placeholder = findWrappedValuePlaceholder(init); - placeholder->setOriginalWrappedValue(arg); + if (auto *param = paramInfo.getPropertyWrapperParam(paramIdx)) { + auto appliedWrapper = appliedPropertyWrappers[appliedWrapperIndex++]; + auto wrapperType = solution.simplifyType(appliedWrapper.wrapperType); + auto initKind = appliedWrapper.initKind; - // Apply the solution to the property wrapper initializer. - auto target = SolutionApplicationTarget(init, cs.DC, CTP_Unused, Type(), - /*isDiscarded=*/false); - auto result = cs.applySolution(solution, target); - if (!result) - return nullptr; + using ValueKind = AppliedPropertyWrapperExpr::ValueKind; + ValueKind valueKind = (initKind == PropertyWrapperInitKind::ProjectedValue ? + ValueKind::ProjectedValue : ValueKind::WrappedValue); - arg = result->getAsExpr(); + arg = AppliedPropertyWrapperExpr::create(ctx, param, arg->getStartLoc(), + wrapperType, arg, valueKind); cs.cacheExprTypes(arg); } diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 8ed0c6631baa8..66e2006175e0e 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -4097,16 +4097,7 @@ ConstraintSystem::applyPropertyWrapperParameter( initKind = PropertyWrapperInitKind::WrappedValue; } - // Build the wrapper initializer expression and generate constraints. - auto *placeholder = PropertyWrapperValuePlaceholderExpr::create( - getASTContext(), anchor->getSourceRange(), Type(), /*wrappedValue*/nullptr); - setType(placeholder, paramType); - - auto *init = buildPropertyWrapperInitCall(param, wrapperType, placeholder, initKind); - setType(init, wrapperType); - generateConstraints(init, DC); - - appliedPropertyWrappers[anchor].push_back({ init }); + appliedPropertyWrappers[anchor].push_back({ wrapperType, initKind }); return getTypeMatchSuccess(); } diff --git a/test/SILGen/property_wrapper_parameter.swift b/test/SILGen/property_wrapper_parameter.swift index bb691c07338c4..fc567a7d6954a 100644 --- a/test/SILGen/property_wrapper_parameter.swift +++ b/test/SILGen/property_wrapper_parameter.swift @@ -29,6 +29,12 @@ public func testSimpleWrapperParameter(@Wrapper value: Int) { _ = _value _ = $value + // property wrapper backing initializer of value #1 in testSimpleWrapperParameter(value:) + // CHECK: sil private [ossa] @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tFACL_SivpfP : $@convention(thin) (Int) -> Wrapper + + // property wrapper init from projected value of value #1 in testSimpleWrapperParameter(value:) + // CHECK: sil private [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 @@ -39,15 +45,15 @@ public func testSimpleWrapperParameter(@Wrapper value: 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_parameter7WrapperV12wrappedValueACyxGx_tcfC : $@convention(method) <τ_0_0> (@in τ_0_0, @thin Wrapper<τ_0_0>.Type) -> @out Wrapper<τ_0_0> + // CHECK: function_ref @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tFACL_SivpfP : $@convention(thin) (Int) -> Wrapper testSimpleWrapperParameter($value: projection) - // CHECK: function_ref @$s26property_wrapper_parameter7WrapperV14projectedValueACyxGAA10ProjectionVyxG_tcfC : $@convention(method) <τ_0_0> (@in Projection<τ_0_0>, @thin Wrapper<τ_0_0>.Type) -> @out Wrapper<τ_0_0> + // CHECK: function_ref @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tFACL_SivpfW : $@convention(thin) (Projection) -> Wrapper } // CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_parameter33testSimpleClosureWrapperParameteryyF : $@convention(thin) () -> () func testSimpleClosureWrapperParameter() { - let closure: (Int) -> Void = { (@Wrapper value) in + let closure: (Int) -> Void = { (@Wrapper value: Int) in _ = value _ = _value _ = $value @@ -61,6 +67,9 @@ func testSimpleClosureWrapperParameter() { // 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 @@ -96,6 +105,12 @@ func testNonMutatingSetter(@NonMutatingSetterWrapper value1: String, @ClassWrapp _ = 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 @@ -127,7 +142,7 @@ struct ProjectionWrapper { // CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_parameter27testImplicitPropertyWrapper10projectionyAA010ProjectionG0VySiG_tF : $@convention(thin) (ProjectionWrapper) -> () func testImplicitPropertyWrapper(projection: ProjectionWrapper) { - let multiStatement: (ProjectionWrapper) -> Void = { $value in + let multiStatement: (ProjectionWrapper) -> Void = { ($value: ProjectionWrapper) in _ = value _ = _value _ = $value @@ -141,13 +156,16 @@ func testImplicitPropertyWrapper(projection: 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 in + let _: (ProjectionWrapper) -> (Int, ProjectionWrapper) = { ($value: ProjectionWrapper) in (value, $value) } @@ -157,6 +175,9 @@ func testImplicitPropertyWrapper(projection: 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 From bea89c6a95ae27173f39b22e07d7a5cb3d50e39b Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 25 Feb 2021 09:13:59 -0800 Subject: [PATCH 34/38] [SILGen] Serialize and give PublicNonABI linkage to property wrapper generator functions for parameters of public functions. --- lib/SIL/IR/SILDeclRef.cpp | 22 +++++++++++--- test/SILGen/property_wrapper_parameter.swift | 30 ++++++++++++++++++-- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index d9860e58f4b9d..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) { diff --git a/test/SILGen/property_wrapper_parameter.swift b/test/SILGen/property_wrapper_parameter.swift index fc567a7d6954a..166a13a72757a 100644 --- a/test/SILGen/property_wrapper_parameter.swift +++ b/test/SILGen/property_wrapper_parameter.swift @@ -30,10 +30,10 @@ public func testSimpleWrapperParameter(@Wrapper value: Int) { _ = $value // property wrapper backing initializer of value #1 in testSimpleWrapperParameter(value:) - // CHECK: sil private [ossa] @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tFACL_SivpfP : $@convention(thin) (Int) -> Wrapper + // 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 private [ossa] @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tFACL_SivpfW : $@convention(thin) (Projection) -> Wrapper + // 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 @@ -184,3 +184,29 @@ func testImplicitPropertyWrapper(projection: 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 +} From c29eecd0b62a01eed8608e5fcb21ca47fa7b1aee Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 25 Feb 2021 15:16:34 -0800 Subject: [PATCH 35/38] [Property Wrappers] Store the callee in AppliedPropertyWrapperExpr to use the correct substitution map when emitting the property wrapper generator application in SILGen. --- include/swift/AST/Expr.h | 11 ++++++++--- lib/AST/Expr.cpp | 5 +++-- lib/SILGen/SILGenExpr.cpp | 8 ++++++-- lib/Sema/CSApply.cpp | 13 +++++++------ test/SILGen/property_wrapper_parameter.swift | 19 +++++++++++++++++++ 5 files changed, 43 insertions(+), 13 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index a292950f2f8bf..84e172b5d4d3f 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -4237,6 +4237,9 @@ class AppliedPropertyWrapperExpr final : public Expr { }; private: + /// The concrete callee which owns the property wrapper. + ConcreteDeclRef Callee; + /// The owning declaration. const ParamDecl *Param; @@ -4249,18 +4252,20 @@ class AppliedPropertyWrapperExpr final : public Expr { /// The kind of value that the property wrapper is applied to. ValueKind Kind; - AppliedPropertyWrapperExpr(const ParamDecl *param, SourceLoc loc, + AppliedPropertyWrapperExpr(ConcreteDeclRef callee, const ParamDecl *param, SourceLoc loc, Type Ty, Expr *value, ValueKind kind) : Expr(ExprKind::AppliedPropertyWrapper, /*Implicit=*/true, Ty), - Param(param), Loc(loc), Value(value), Kind(kind) {} + Callee(callee), Param(param), Loc(loc), Value(value), Kind(kind) {} public: static AppliedPropertyWrapperExpr * - create(ASTContext &ctx, const ParamDecl *param, SourceLoc loc, + 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; }; diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 912b48a2d6574..c1abf42881ed6 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1510,10 +1510,11 @@ PropertyWrapperValuePlaceholderExpr::create(ASTContext &ctx, SourceRange range, } AppliedPropertyWrapperExpr * -AppliedPropertyWrapperExpr::create(ASTContext &ctx, const ParamDecl *param, +AppliedPropertyWrapperExpr::create(ASTContext &ctx, ConcreteDeclRef callee, + const ParamDecl *param, SourceLoc loc, Type Ty, Expr *value, AppliedPropertyWrapperExpr::ValueKind kind) { - return new (ctx) AppliedPropertyWrapperExpr(param, loc, Ty, value, kind); + return new (ctx) AppliedPropertyWrapperExpr(callee, param, loc, Ty, value, kind); } const ParamDecl *DefaultArgumentExpr::getParamDecl() const { diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 2cd1f6ece8d40..6682d6a636a65 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -5248,9 +5248,13 @@ RValue RValueEmitter::visitAppliedPropertyWrapperExpr( break; } + SubstitutionMap subs; + if (param->getDeclContext()->getAsDecl()) { + subs = E->getCallee().getSubstitutions(); + } + return SGF.emitApplyOfPropertyWrapperBackingInitializer( - SILLocation(E), param, SGF.getForwardingSubstitutionMap(), - std::move(argument), initKind); + SILLocation(E), param, subs, std::move(argument), initKind); } ProtocolDecl *SILGenFunction::getPointerProtocol() { diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 98f136053e833..06f3bbb17dd44 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -679,7 +679,7 @@ namespace { declRefExpr->getFunctionRefKind() == FunctionRefKind::Unapplied)) { auto &appliedWrappers = solution.appliedPropertyWrappers[locator.getAnchor()]; result = buildPropertyWrapperFnThunk(result, fullType->getAs(), fnDecl, - appliedWrappers); + ref, appliedWrappers); } } @@ -978,7 +978,7 @@ namespace { } AutoClosureExpr *buildPropertyWrapperFnThunk( - Expr *fnRef, FunctionType *fnType, AnyFunctionRef fnDecl, + Expr *fnRef, FunctionType *fnType, AnyFunctionRef fnDecl, ConcreteDeclRef ref, ArrayRef appliedPropertyWrappers) { auto &context = cs.getASTContext(); auto paramInfo = fnType->getParams(); @@ -1018,7 +1018,7 @@ namespace { ValueKind valueKind = (initKind == PropertyWrapperInitKind::ProjectedValue ? ValueKind::ProjectedValue : ValueKind::WrappedValue); - paramRef = AppliedPropertyWrapperExpr::create(context, innerParam, + paramRef = AppliedPropertyWrapperExpr::create(context, ref, innerParam, innerParam->getStartLoc(), wrapperType, paramRef, valueKind); cs.cacheExprTypes(paramRef); @@ -1160,8 +1160,9 @@ namespace { 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, appliedWrappers); + fnDecl, callee, appliedWrappers); ref->setType(FunctionType::get(refTy->getParams(), selfCall->getType())); cs.cacheType(ref); @@ -5841,7 +5842,7 @@ Expr *ExprRewriter::coerceCallArguments( ValueKind valueKind = (initKind == PropertyWrapperInitKind::ProjectedValue ? ValueKind::ProjectedValue : ValueKind::WrappedValue); - arg = AppliedPropertyWrapperExpr::create(ctx, param, arg->getStartLoc(), + arg = AppliedPropertyWrapperExpr::create(ctx, callee, param, arg->getStartLoc(), wrapperType, arg, valueKind); cs.cacheExprTypes(arg); } @@ -7852,7 +7853,7 @@ namespace { auto &appliedWrappers = Rewriter.solution.appliedPropertyWrappers[closure]; return Rewriter.buildPropertyWrapperFnThunk(closure, closureFnType, closure, - appliedWrappers); + ConcreteDeclRef(), appliedWrappers); } /// Rewrite the function for the given solution. diff --git a/test/SILGen/property_wrapper_parameter.swift b/test/SILGen/property_wrapper_parameter.swift index 166a13a72757a..5045ca7882997 100644 --- a/test/SILGen/property_wrapper_parameter.swift +++ b/test/SILGen/property_wrapper_parameter.swift @@ -51,6 +51,25 @@ func simpleWrapperParameterCaller(projection: 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 From db387273f662d5f7a6bc6a55a6173147d4c357d0 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 25 Feb 2021 17:42:27 -0800 Subject: [PATCH 36/38] [ConstraintSystem] Don't allow dollar prefixes in argument labels unless the parameter has an attached property wrapper. --- include/swift/AST/DiagnosticsSema.def | 4 ++++ lib/Sema/CSDiagnostics.cpp | 10 ++++++--- lib/Sema/CSGen.cpp | 12 +++++++++++ lib/Sema/CSSimplify.cpp | 7 ++++--- lib/Sema/ConstraintSystem.cpp | 21 +++++++++---------- .../property_wrapper_parameter_invalid.swift | 10 +++++++++ 6 files changed, 47 insertions(+), 17 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index e32b8ad7b3ce2..484be7341f99e 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5490,6 +5490,10 @@ NOTE(property_wrapper_declared_here,none, 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)) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 77aa6c13c6d2c..45b0d382c7313 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -3310,10 +3310,14 @@ bool MissingProjectedValueFailure::diagnoseAsError() { } bool MissingPropertyWrapperAttributeFailure::diagnoseAsError() { - emitDiagnostic(diag::invalid_implicit_property_wrapper, wrapperType); + 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. + // 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; } diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 66e2006175e0e..b0659a5e76945 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -4061,6 +4061,18 @@ ConstraintSystem::applyPropertyWrapperParameter( 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 { diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index f3ca8f36477b0..7f20b596b3009 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -1420,9 +1420,10 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( cs, cs.getConstraintLocator(loc))); } - if (auto *param = paramInfo.getPropertyWrapperParam(argIdx)) { - auto argLabel = argInfo->Labels[argIdx]; - if (cs.applyPropertyWrapperParameter(paramTy, argTy, const_cast(param), + auto *wrappedParam = paramInfo.getPropertyWrapperParam(argIdx); + auto argLabel = argument.getLabel(); + if (wrappedParam || argLabel.hasDollarPrefix()) { + if (cs.applyPropertyWrapperParameter(paramTy, argTy, const_cast(wrappedParam), argLabel, subKind, locator).isFailure()) { return cs.getTypeMatchFailure(loc); } diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index d8f9211e38175..8e0789be375b8 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -1162,10 +1162,8 @@ static FunctionType * unwrapPropertyWrapperParameterTypes(ConstraintSystem &cs, AbstractFunctionDecl *funcDecl, FunctionRefKind functionRefKind, FunctionType *functionType, ConstraintLocatorBuilder locator) { - // Only apply property wrappers to unapplied references to functions - // with wrapped parameters. - if (!AnyFunctionRef(funcDecl).hasPropertyWrapperParameters() || - !(functionRefKind == FunctionRefKind::Compound || + // Only apply property wrappers to unapplied references to functions. + if (!(functionRefKind == FunctionRefKind::Compound || functionRefKind == FunctionRefKind::Unapplied)) { return functionType; } @@ -1187,12 +1185,6 @@ unwrapPropertyWrapperParameterTypes(ConstraintSystem &cs, AbstractFunctionDecl * } for (unsigned i : indices(*paramList)) { - auto *paramDecl = paramList->get(i); - if (!paramDecl->hasAttachedPropertyWrapper()) { - adjustedParamTypes.push_back(paramTypes[i]); - continue; - } - Identifier argLabel; if (functionRefKind == FunctionRefKind::Compound) { auto &context = cs.getASTContext(); @@ -1201,6 +1193,12 @@ unwrapPropertyWrapperParameterTypes(ConstraintSystem &cs, AbstractFunctionDecl * 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(); @@ -1209,7 +1207,8 @@ unwrapPropertyWrapperParameterTypes(ConstraintSystem &cs, AbstractFunctionDecl * ConstraintKind::Equal, locator); } - return FunctionType::get(adjustedParamTypes, functionType->getResult()); + return FunctionType::get(adjustedParamTypes, functionType->getResult(), + functionType->getExtInfo()); } std::pair diff --git a/test/Sema/property_wrapper_parameter_invalid.swift b/test/Sema/property_wrapper_parameter_invalid.swift index efbfe0dddb57c..313facea3ca5d 100644 --- a/test/Sema/property_wrapper_parameter_invalid.swift +++ b/test/Sema/property_wrapper_parameter_invalid.swift @@ -73,3 +73,13 @@ 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) +} From b7a170cd77a16b64c6fee7944c536de8e20321e0 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 25 Feb 2021 17:44:51 -0800 Subject: [PATCH 37/38] [NFC][ConstraintSystem] Rename applyPropertyWrapperParameter. --- include/swift/Sema/ConstraintSystem.h | 2 +- lib/Sema/CSGen.cpp | 2 +- lib/Sema/CSSimplify.cpp | 10 +++++----- lib/Sema/ConstraintSystem.cpp | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 814f6c14b8b3c..baf02bfd6e252 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -4644,7 +4644,7 @@ class ConstraintSystem { /// Matches a wrapped or projected value parameter type to its backing /// property wrapper type by applying the property wrapper. - TypeMatchResult applyPropertyWrapperParameter( + TypeMatchResult applyPropertyWrapperToParameter( Type wrapperType, Type paramType, ParamDecl *param, Identifier argLabel, ConstraintKind matchKind, ConstraintLocatorBuilder locator); diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index b0659a5e76945..158f0033094f4 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -4053,7 +4053,7 @@ bool ConstraintSystem::generateConstraints( } ConstraintSystem::TypeMatchResult -ConstraintSystem::applyPropertyWrapperParameter( +ConstraintSystem::applyPropertyWrapperToParameter( Type wrapperType, Type paramType, ParamDecl *param, Identifier argLabel, ConstraintKind matchKind, ConstraintLocatorBuilder locator) { Expr *anchor = getAsExpr(locator.getAnchor()); diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 7f20b596b3009..edd089bd5b923 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -1423,8 +1423,8 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( auto *wrappedParam = paramInfo.getPropertyWrapperParam(argIdx); auto argLabel = argument.getLabel(); if (wrappedParam || argLabel.hasDollarPrefix()) { - if (cs.applyPropertyWrapperParameter(paramTy, argTy, const_cast(wrappedParam), - argLabel, subKind, locator).isFailure()) { + if (cs.applyPropertyWrapperToParameter(paramTy, argTy, const_cast(wrappedParam), + argLabel, subKind, locator).isFailure()) { return cs.getTypeMatchFailure(loc); } continue; @@ -8228,9 +8228,9 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, } if (!hasError) { - auto result = applyPropertyWrapperParameter(backingType, param.getParameterType(), - paramDecl, paramDecl->getName(), - ConstraintKind::Equal, locator); + auto result = applyPropertyWrapperToParameter(backingType, param.getParameterType(), + paramDecl, paramDecl->getName(), + ConstraintKind::Equal, locator); if (result.isFailure()) return false; diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 8e0789be375b8..7e5cd9172eae3 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -1203,8 +1203,8 @@ unwrapPropertyWrapperParameterTypes(ConstraintSystem &cs, AbstractFunctionDecl * auto paramType = paramTypes[i].getParameterType(); auto paramLabel = paramTypes[i].getLabel(); adjustedParamTypes.push_back(AnyFunctionType::Param(wrappedType, paramLabel)); - cs.applyPropertyWrapperParameter(paramType, wrappedType, paramDecl, argLabel, - ConstraintKind::Equal, locator); + cs.applyPropertyWrapperToParameter(paramType, wrappedType, paramDecl, argLabel, + ConstraintKind::Equal, locator); } return FunctionType::get(adjustedParamTypes, functionType->getResult(), From b3d54b67fcc51e5458ee63a439ec94d911b00e97 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 25 Feb 2021 18:25:18 -0800 Subject: [PATCH 38/38] [Property Wrappers] Don't allow parameters with attached property wrappers in protocol requirements. --- include/swift/AST/DiagnosticsSema.def | 4 ++-- lib/Sema/TypeCheckPropertyWrapper.cpp | 6 +++++- test/Sema/property_wrapper_parameter_invalid.swift | 5 +++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 484be7341f99e..d32febf61bfb8 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5524,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/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index 2a4384a55c48f..d0db912396b71 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -455,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) || @@ -468,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; diff --git a/test/Sema/property_wrapper_parameter_invalid.swift b/test/Sema/property_wrapper_parameter_invalid.swift index 313facea3ca5d..6c84263b5337c 100644 --- a/test/Sema/property_wrapper_parameter_invalid.swift +++ b/test/Sema/property_wrapper_parameter_invalid.swift @@ -83,3 +83,8 @@ func testInvalidArgLabel() { // 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) +}