| //===--- TypeCheckPropertyWrapper.cpp - property wrappers ---------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements semantic analysis for property wrappers. |
| // |
| //===----------------------------------------------------------------------===// |
| #include "ConstraintSystem.h" |
| #include "TypeChecker.h" |
| #include "TypeCheckType.h" |
| #include "swift/AST/ASTContext.h" |
| #include "swift/AST/Decl.h" |
| #include "swift/AST/DiagnosticsSema.h" |
| #include "swift/AST/LazyResolver.h" |
| #include "swift/AST/NameLookupRequests.h" |
| #include "swift/AST/ParameterList.h" |
| #include "swift/AST/PropertyWrappers.h" |
| #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:)`) |
| InitialValue, |
| /// An wrapped-value initializer (i.e. `init(wrappedValue:)`) |
| WrappedValue, |
| /// An default-value initializer (i.e. `init()` or `init(defaultArgs...)`) |
| Default |
| }; |
| |
| /// Find the named property in a property wrapper to which access will |
| /// be delegated. |
| static VarDecl *findValueProperty(ASTContext &ctx, NominalTypeDecl *nominal, |
| Identifier name, bool allowMissing) { |
| SmallVector<VarDecl *, 2> vars; |
| { |
| SmallVector<ValueDecl *, 2> decls; |
| nominal->lookupQualified(nominal, name, NL_QualifiedDefault, decls); |
| for (const auto &foundDecl : decls) { |
| auto foundVar = dyn_cast<VarDecl>(foundDecl); |
| if (!foundVar || foundVar->isStatic() || |
| foundVar->getDeclContext() != nominal) |
| continue; |
| |
| vars.push_back(foundVar); |
| } |
| } |
| |
| // Diagnose missing or ambiguous properties. |
| switch (vars.size()) { |
| case 0: |
| if (!allowMissing) { |
| nominal->diagnose(diag::property_wrapper_no_value_property, |
| nominal->getDeclaredType(), name); |
| } |
| return nullptr; |
| |
| case 1: |
| break; |
| |
| default: |
| nominal->diagnose(diag::property_wrapper_ambiguous_value_property, |
| nominal->getDeclaredType(), name); |
| for (auto var : vars) { |
| var->diagnose(diag::kind_declname_declared_here, |
| var->getDescriptiveKind(), var->getFullName()); |
| } |
| return nullptr; |
| } |
| |
| // The property must be as accessible as the nominal type. |
| VarDecl *var = vars.front(); |
| if (var->getFormalAccess() < nominal->getFormalAccess()) { |
| var->diagnose(diag::property_wrapper_type_requirement_not_accessible, |
| var->getFormalAccess(), var->getDescriptiveKind(), |
| var->getFullName(), nominal->getDeclaredType(), |
| nominal->getFormalAccess()); |
| return nullptr; |
| } |
| |
| return var; |
| } |
| |
| /// Determine whether we have a suitable initializer within a property wrapper |
| /// type. |
| static ConstructorDecl * |
| findSuitableWrapperInit(ASTContext &ctx, NominalTypeDecl *nominal, |
| VarDecl *valueVar, PropertyWrapperInitKind initKind) { |
| enum class NonViableReason { |
| Failable, |
| ParameterTypeMismatch, |
| Inaccessible, |
| }; |
| |
| SmallVector<std::tuple<ConstructorDecl *, NonViableReason, Type>, 2> |
| nonviable; |
| SmallVector<ConstructorDecl *, 2> viableInitializers; |
| SmallVector<ValueDecl *, 2> decls; |
| |
| Identifier argumentLabel; |
| switch (initKind) { |
| case PropertyWrapperInitKind::InitialValue: |
| argumentLabel = ctx.Id_initialValue; |
| break; |
| case PropertyWrapperInitKind::WrappedValue: |
| argumentLabel = ctx.Id_wrappedValue; |
| break; |
| case PropertyWrapperInitKind::Default: |
| break; |
| } |
| |
| nominal->lookupQualified(nominal, DeclBaseName::createConstructor(), |
| NL_QualifiedDefault, decls); |
| for (const auto &decl : decls) { |
| auto init = dyn_cast<ConstructorDecl>(decl); |
| if (!init || init->getDeclContext() != nominal || init->isGeneric()) |
| continue; |
| |
| ParamDecl *argumentParam = nullptr; |
| bool hasExtraneousParam = false; |
| // Check whether every parameter meets one of the following criteria: |
| // (1) The parameter has a default argument, or |
| // (2) The parameter has the given argument label. |
| for (auto param : *init->getParameters()) { |
| // Recognize the first parameter with the requested argument label. |
| if (!argumentLabel.empty() && param->getArgumentName() == argumentLabel && |
| !argumentParam) { |
| argumentParam = param; |
| continue; |
| } |
| |
| if (param->isDefaultArgument()) |
| continue; |
| |
| // Skip this init as the param doesn't meet the above criteria |
| hasExtraneousParam = true; |
| break; |
| } |
| |
| if (hasExtraneousParam) |
| continue; |
| |
| // Failable initializers cannot be used. |
| if (init->isFailable()) { |
| nonviable.push_back( |
| std::make_tuple(init, NonViableReason::Failable, Type())); |
| continue; |
| } |
| |
| // Check accessibility. |
| if (init->getFormalAccess() < nominal->getFormalAccess()) { |
| nonviable.push_back( |
| std::make_tuple(init, NonViableReason::Inaccessible, Type())); |
| continue; |
| } |
| |
| // Additional checks for initial-value and wrapped-value initializers |
| if (initKind != PropertyWrapperInitKind::Default) { |
| if (!argumentParam) |
| continue; |
| |
| if (!argumentParam->hasInterfaceType()) |
| continue; |
| |
| if (argumentParam->isInOut() || argumentParam->isVariadic()) |
| continue; |
| |
| auto paramType = argumentParam->getInterfaceType(); |
| if (argumentParam->isAutoClosure()) { |
| if (auto *fnType = paramType->getAs<FunctionType>()) |
| paramType = fnType->getResult(); |
| } |
| |
| // The parameter type must be the same as the type of `valueVar` or an |
| // autoclosure thereof. |
| if (!paramType->isEqual(valueVar->getValueInterfaceType())) { |
| nonviable.push_back(std::make_tuple( |
| init, NonViableReason::ParameterTypeMismatch, paramType)); |
| continue; |
| } |
| } |
| |
| viableInitializers.push_back(init); |
| } |
| |
| // If we found some nonviable candidates but no viable ones, complain. |
| if (viableInitializers.empty() && !nonviable.empty()) { |
| for (const auto &candidate : nonviable) { |
| auto init = std::get<0>(candidate); |
| auto reason = std::get<1>(candidate); |
| auto paramType = std::get<2>(candidate); |
| switch (reason) { |
| case NonViableReason::Failable: |
| init->diagnose(diag::property_wrapper_failable_init, |
| init->getFullName()); |
| break; |
| |
| case NonViableReason::Inaccessible: |
| init->diagnose(diag::property_wrapper_type_requirement_not_accessible, |
| init->getFormalAccess(), init->getDescriptiveKind(), |
| init->getFullName(), nominal->getDeclaredType(), |
| nominal->getFormalAccess()); |
| break; |
| |
| case NonViableReason::ParameterTypeMismatch: |
| init->diagnose(diag::property_wrapper_wrong_initial_value_init, |
| init->getFullName(), paramType, |
| valueVar->getValueInterfaceType()); |
| valueVar->diagnose(diag::decl_declared_here, valueVar->getFullName()); |
| break; |
| } |
| } |
| } |
| |
| return viableInitializers.empty() ? nullptr : viableInitializers.front(); |
| } |
| |
| /// Determine whether we have a suitable static subscript to which we |
| /// can pass along the enclosing self + key-paths. |
| static SubscriptDecl *findEnclosingSelfSubscript(ASTContext &ctx, |
| NominalTypeDecl *nominal, |
| Identifier propertyName) { |
| Identifier argNames[] = { |
| ctx.Id_enclosingInstance, |
| propertyName, |
| ctx.Id_storage |
| }; |
| DeclName subscriptName(ctx, DeclBaseName::createSubscript(), argNames); |
| |
| SmallVector<SubscriptDecl *, 2> subscripts; |
| for (auto member : nominal->lookupDirect(subscriptName)) { |
| auto subscript = dyn_cast<SubscriptDecl>(member); |
| if (!subscript) |
| continue; |
| |
| if (subscript->isInstanceMember()) |
| continue; |
| |
| if (subscript->getDeclContext() != nominal) |
| continue; |
| |
| subscripts.push_back(subscript); |
| } |
| |
| switch (subscripts.size()) { |
| case 0: |
| return nullptr; |
| |
| case 1: |
| break; |
| |
| default: |
| // Diagnose ambiguous init() initializers. |
| nominal->diagnose(diag::property_wrapper_ambiguous_enclosing_self_subscript, |
| nominal->getDeclaredType(), subscriptName); |
| for (auto subscript : subscripts) { |
| subscript->diagnose(diag::kind_declname_declared_here, |
| subscript->getDescriptiveKind(), |
| subscript->getFullName()); |
| } |
| return nullptr; |
| |
| } |
| |
| auto subscript = subscripts.front(); |
| // the subscript must be as accessible as the nominal type. |
| if (subscript->getFormalAccess() < nominal->getFormalAccess()) { |
| subscript->diagnose(diag::property_wrapper_type_requirement_not_accessible, |
| subscript->getFormalAccess(), |
| subscript->getDescriptiveKind(), |
| subscript->getFullName(), nominal->getDeclaredType(), |
| nominal->getFormalAccess()); |
| return nullptr; |
| } |
| |
| return subscript; |
| } |
| |
| llvm::Expected<PropertyWrapperTypeInfo> |
| PropertyWrapperTypeInfoRequest::evaluate( |
| Evaluator &eval, NominalTypeDecl *nominal) const { |
| // We must have the @propertyWrapper attribute to continue. |
| if (!nominal->getAttrs().hasAttribute<PropertyWrapperAttr>()) { |
| return PropertyWrapperTypeInfo(); |
| } |
| |
| // Look for a non-static property named "wrappedValue" in the property |
| // wrapper type. |
| ASTContext &ctx = nominal->getASTContext(); |
| auto valueVar = |
| findValueProperty(ctx, nominal, ctx.Id_wrappedValue, |
| /*allowMissing=*/false); |
| if (!valueVar) |
| return PropertyWrapperTypeInfo(); |
| |
| // FIXME: Remove this one |
| (void)valueVar->getInterfaceType(); |
| |
| PropertyWrapperTypeInfo result; |
| result.valueVar = valueVar; |
| if (findSuitableWrapperInit(ctx, nominal, valueVar, |
| PropertyWrapperInitKind::WrappedValue)) |
| result.wrappedValueInit = PropertyWrapperTypeInfo::HasWrappedValueInit; |
| else if (auto init = findSuitableWrapperInit( |
| ctx, nominal, valueVar, PropertyWrapperInitKind::InitialValue)) { |
| result.wrappedValueInit = PropertyWrapperTypeInfo::HasInitialValueInit; |
| |
| if (init->getLoc().isValid()) { |
| auto diag = init->diagnose(diag::property_wrapper_init_initialValue); |
| for (auto param : *init->getParameters()) { |
| if (param->getArgumentName() == ctx.Id_initialValue) { |
| if (param->getArgumentNameLoc().isValid()) |
| diag.fixItReplace(param->getArgumentNameLoc(), "wrappedValue"); |
| else |
| diag.fixItInsert(param->getLoc(), "wrappedValue "); |
| break; |
| } |
| } |
| } |
| } |
| |
| if (findSuitableWrapperInit(ctx, nominal, /*valueVar=*/nullptr, |
| PropertyWrapperInitKind::Default)) { |
| result.defaultInit = PropertyWrapperTypeInfo::HasDefaultValueInit; |
| } |
| |
| result.projectedValueVar = |
| findValueProperty(ctx, nominal, ctx.Id_projectedValue, |
| /*allowMissing=*/true); |
| result.enclosingInstanceWrappedSubscript = |
| findEnclosingSelfSubscript(ctx, nominal, ctx.Id_wrapped); |
| result.enclosingInstanceProjectedSubscript = |
| findEnclosingSelfSubscript(ctx, nominal, ctx.Id_projected); |
| |
| // If there was no projectedValue property, but there is a wrapperValue, |
| // property, use that and warn. |
| if (!result.projectedValueVar) { |
| result.projectedValueVar = |
| findValueProperty(ctx, nominal, ctx.Id_wrapperValue, |
| /*allowMissing=*/true); |
| if (result.projectedValueVar && |
| result.projectedValueVar->getLoc().isValid()) { |
| result.projectedValueVar->diagnose(diag::property_wrapper_wrapperValue) |
| .fixItReplace(result.projectedValueVar->getNameLoc(), |
| "projectedValue"); |
| } |
| } |
| |
| return result; |
| } |
| |
| llvm::Expected<llvm::TinyPtrVector<CustomAttr *>> |
| AttachedPropertyWrappersRequest::evaluate(Evaluator &evaluator, |
| VarDecl *var) const { |
| ASTContext &ctx = var->getASTContext(); |
| auto dc = var->getDeclContext(); |
| llvm::TinyPtrVector<CustomAttr *> result; |
| for (auto attr : var->getAttrs().getAttributes<CustomAttr>()) { |
| auto mutableAttr = const_cast<CustomAttr *>(attr); |
| // Figure out which nominal declaration this custom attribute refers to. |
| auto nominal = evaluateOrDefault( |
| ctx.evaluator, CustomAttrNominalRequest{mutableAttr, dc}, nullptr); |
| |
| // If we didn't find a nominal type with a @propertyWrapper attribute, |
| // skip this custom attribute. |
| if (!nominal || !nominal->getAttrs().hasAttribute<PropertyWrapperAttr>()) |
| continue; |
| |
| // If the declaration came from a module file, we've already done all of |
| // the semantic checking required. |
| auto sourceFile = dc->getParentSourceFile(); |
| if (!sourceFile) { |
| result.push_back(mutableAttr); |
| continue; |
| } |
| |
| // Check various restrictions on which properties can have wrappers |
| // attached to them. |
| |
| // Local properties do not yet support wrappers. |
| if (var->getDeclContext()->isLocalContext()) { |
| ctx.Diags.diagnose(attr->getLocation(), diag::property_wrapper_local); |
| continue; |
| } |
| |
| // Nor does top-level code. |
| if (var->getDeclContext()->isModuleScopeContext()) { |
| ctx.Diags.diagnose(attr->getLocation(), diag::property_wrapper_top_level); |
| continue; |
| } |
| |
| // Check that the variable is part of a single-variable pattern. |
| auto binding = var->getParentPatternBinding(); |
| if (!binding || binding->getSingleVar() != var) { |
| ctx.Diags.diagnose(attr->getLocation(), |
| diag::property_wrapper_not_single_var); |
| continue; |
| } |
| |
| // A property wrapper cannot be attached to a 'let'. |
| if (var->isLet()) { |
| ctx.Diags.diagnose(attr->getLocation(), diag::property_wrapper_let); |
| continue; |
| } |
| |
| // Check for conflicting attributes. |
| if (var->getAttrs().hasAttribute<LazyAttr>() || |
| var->getAttrs().hasAttribute<NSCopyingAttr>() || |
| var->getAttrs().hasAttribute<NSManagedAttr>() || |
| (var->getAttrs().hasAttribute<ReferenceOwnershipAttr>() && |
| var->getAttrs().getAttribute<ReferenceOwnershipAttr>()->get() != |
| ReferenceOwnership::Strong)) { |
| int whichKind; |
| if (var->getAttrs().hasAttribute<LazyAttr>()) |
| whichKind = 0; |
| else if (var->getAttrs().hasAttribute<NSCopyingAttr>()) |
| whichKind = 1; |
| else if (var->getAttrs().hasAttribute<NSManagedAttr>()) |
| whichKind = 2; |
| else { |
| auto attr = var->getAttrs().getAttribute<ReferenceOwnershipAttr>(); |
| whichKind = 2 + static_cast<unsigned>(attr->get()); |
| } |
| var->diagnose(diag::property_with_wrapper_conflict_attribute, |
| var->getFullName(), whichKind); |
| continue; |
| } |
| |
| // A property with a wrapper cannot be declared in a protocol, enum, or |
| // an extension. |
| if (isa<ProtocolDecl>(dc) || |
| (isa<ExtensionDecl>(dc) && var->isInstanceMember()) || |
| (isa<EnumDecl>(dc) && var->isInstanceMember())) { |
| int whichKind; |
| if (isa<ProtocolDecl>(dc)) |
| whichKind = 0; |
| else if (isa<ExtensionDecl>(dc)) |
| whichKind = 1; |
| else |
| whichKind = 2; |
| var->diagnose(diag::property_with_wrapper_in_bad_context, |
| var->getFullName(), whichKind) |
| .highlight(attr->getRange()); |
| |
| continue; |
| } |
| |
| // Properties with wrappers must not override another property. |
| if (auto classDecl = dyn_cast<ClassDecl>(dc)) { |
| if (auto overrideAttr = var->getAttrs().getAttribute<OverrideAttr>()) { |
| var->diagnose(diag::property_with_wrapper_overrides, |
| var->getFullName()) |
| .highlight(attr->getRange()); |
| continue; |
| } |
| } |
| |
| result.push_back(mutableAttr); |
| } |
| |
| // Attributes are stored in reverse order in the AST, but we want them in |
| // source order so that the outermost property wrapper comes first. |
| std::reverse(result.begin(), result.end()); |
| return result; |
| } |
| |
| llvm::Expected<Type> |
| AttachedPropertyWrapperTypeRequest::evaluate(Evaluator &evaluator, |
| VarDecl *var, |
| unsigned index) const { |
| // Find the custom attributes for the attached property wrapper. |
| llvm::Expected<llvm::TinyPtrVector<CustomAttr *>> customAttrVal = |
| evaluator(AttachedPropertyWrappersRequest{var}); |
| if (!customAttrVal) |
| return customAttrVal.takeError(); |
| |
| // If there isn't an attached property wrapper at this index, we're done. |
| if (index >= customAttrVal->size()) |
| return Type(); |
| |
| auto customAttr = (*customAttrVal)[index]; |
| if (!customAttr) |
| return Type(); |
| |
| ASTContext &ctx = var->getASTContext(); |
| if (!ctx.getLazyResolver()) |
| return nullptr; |
| |
| auto resolution = |
| TypeResolution::forContextual(var->getDeclContext()); |
| TypeResolutionOptions options(TypeResolverContext::PatternBindingDecl); |
| options |= TypeResolutionFlags::AllowUnboundGenerics; |
| |
| auto &tc = *static_cast<TypeChecker *>(ctx.getLazyResolver()); |
| if (TypeChecker::validateType(tc.Context, customAttr->getTypeLoc(), |
| resolution, options)) |
| return ErrorType::get(ctx); |
| |
| return customAttr->getTypeLoc().getType(); |
| } |
| |
| llvm::Expected<Type> |
| PropertyWrapperBackingPropertyTypeRequest::evaluate( |
| Evaluator &evaluator, VarDecl *var) const { |
| llvm::Expected<Type> rawTypeResult = |
| evaluator(AttachedPropertyWrapperTypeRequest{var, 0}); |
| if (!rawTypeResult) |
| return rawTypeResult; |
| |
| Type rawType = *rawTypeResult; |
| if (!rawType || rawType->hasError()) |
| return Type(); |
| |
| if (!rawType->hasUnboundGenericType()) |
| return rawType->mapTypeOutOfContext(); |
| |
| auto binding = var->getParentPatternBinding(); |
| if (!binding) |
| return Type(); |
| |
| ASTContext &ctx = var->getASTContext(); |
| if (!ctx.getLazyResolver()) |
| return Type(); |
| |
| // If there's an initializer of some sort, checking it will determine the |
| // property wrapper type. |
| unsigned index = binding->getPatternEntryIndexForVarDecl(var); |
| TypeChecker &tc = *static_cast<TypeChecker *>(ctx.getLazyResolver()); |
| if (binding->isInitialized(index)) { |
| // FIXME(InterfaceTypeRequest): Remove this. |
| (void)var->getInterfaceType(); |
| if (!binding->isInitializerChecked(index)) |
| tc.typeCheckPatternBinding(binding, index); |
| |
| Type type = ctx.getSideCachedPropertyWrapperBackingPropertyType(var); |
| assert(type || ctx.Diags.hadAnyError()); |
| return type; |
| } |
| |
| // Compute the type of the property to plug in to the wrapper type. |
| // FIXME(InterfaceTypeRequest): Remove this. |
| (void)var->getInterfaceType(); |
| Type propertyType = var->getType(); |
| if (!propertyType || propertyType->hasError()) |
| return Type(); |
| |
| using namespace constraints; |
| auto dc = var->getInnermostDeclContext(); |
| ConstraintSystem cs(tc, dc, None); |
| auto emptyLocator = cs.getConstraintLocator(nullptr); |
| |
| auto wrapperAttrs = var->getAttachedPropertyWrappers(); |
| Type valueMemberType; |
| Type outermostOpenedWrapperType; |
| for (unsigned i : indices(wrapperAttrs)) { |
| Type rawWrapperType = var->getAttachedPropertyWrapperType(i); |
| if (!rawWrapperType) |
| return Type(); |
| |
| // Open the type. |
| Type openedWrapperType = |
| cs.openUnboundGenericType(rawWrapperType, emptyLocator); |
| if (!outermostOpenedWrapperType) |
| outermostOpenedWrapperType = openedWrapperType; |
| |
| // If we already have a value member type, it must be equivalent to |
| // this opened wrapper type. |
| if (valueMemberType) { |
| cs.addConstraint(ConstraintKind::Equal, valueMemberType, |
| openedWrapperType, emptyLocator); |
| } |
| |
| // Retrieve the type of the wrapped value. |
| auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(i); |
| if (!wrapperInfo) |
| return Type(); |
| |
| valueMemberType = openedWrapperType->getTypeOfMember( |
| dc->getParentModule(), wrapperInfo.valueVar); |
| } |
| |
| // The resulting value member type must be equivalent to the property |
| // type. |
| cs.addConstraint(ConstraintKind::Equal, valueMemberType, |
| propertyType, emptyLocator); |
| |
| SmallVector<Solution, 4> solutions; |
| if (cs.solve(nullptr, solutions) || solutions.size() != 1) { |
| var->diagnose(diag::property_wrapper_incompatible_property, |
| propertyType, rawType); |
| if (auto nominalWrapper = rawType->getAnyNominal()) { |
| nominalWrapper->diagnose(diag::property_wrapper_declared_here, |
| nominalWrapper->getFullName()); |
| } |
| return Type(); |
| } |
| |
| Type wrapperType = solutions.front().simplifyType(outermostOpenedWrapperType); |
| return wrapperType->mapTypeOutOfContext(); |
| } |
| |
| Type swift::computeWrappedValueType(VarDecl *var, Type backingStorageType, |
| Optional<unsigned> limit) { |
| auto wrapperAttrs = var->getAttachedPropertyWrappers(); |
| unsigned realLimit = 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); |
| if (!wrappedInfo) |
| return wrappedValueType; |
| |
| wrappedValueType = wrappedValueType->getTypeOfMember( |
| dc->getParentModule(), |
| wrappedInfo.valueVar, |
| wrappedInfo.valueVar->getValueInterfaceType()); |
| if (wrappedValueType->hasError()) |
| break; |
| } |
| |
| return wrappedValueType; |
| } |
| |
| Expr *swift::buildPropertyWrapperInitialValueCall( |
| VarDecl *var, Type backingStorageType, Expr *value, |
| bool ignoreAttributeArgs) { |
| // From the innermost wrapper type out, form init(wrapperValue:) calls. |
| ASTContext &ctx = var->getASTContext(); |
| auto wrapperAttrs = var->getAttachedPropertyWrappers(); |
| Expr *initializer = value; |
| for (unsigned i : llvm::reverse(indices(wrapperAttrs))) { |
| Type wrapperType = |
| backingStorageType ? computeWrappedValueType(var, backingStorageType, i) |
| : var->getAttachedPropertyWrapperType(i); |
| if (!wrapperType) |
| return nullptr; |
| |
| auto typeExpr = TypeExpr::createImplicitHack( |
| wrapperAttrs[i]->getTypeLoc().getLoc(), |
| wrapperType, ctx); |
| |
| SourceLoc startLoc = wrapperAttrs[i]->getTypeLoc().getSourceRange().Start; |
| |
| // If there were no arguments provided for the attribute at this level, |
| // call `init(wrappedValue:)` directly. |
| auto attr = wrapperAttrs[i]; |
| if (!attr->getArg() || ignoreAttributeArgs) { |
| Identifier argName; |
| switch (var->getAttachedPropertyWrapperTypeInfo(i).wrappedValueInit) { |
| case PropertyWrapperTypeInfo::HasInitialValueInit: |
| argName = ctx.Id_initialValue; |
| break; |
| |
| case PropertyWrapperTypeInfo::HasWrappedValueInit: |
| case PropertyWrapperTypeInfo::NoWrappedValueInit: |
| argName = ctx.Id_wrappedValue; |
| break; |
| } |
| |
| auto endLoc = initializer->getEndLoc(); |
| if (endLoc.isInvalid() && startLoc.isValid()) |
| endLoc = wrapperAttrs[i]->getTypeLoc().getSourceRange().End; |
| |
| initializer = CallExpr::create( |
| ctx, typeExpr, startLoc, {initializer}, {argName}, |
| {initializer->getStartLoc()}, endLoc, |
| nullptr, /*implicit=*/true); |
| continue; |
| } |
| |
| // Splice `wrappedValue:` into the argument list. |
| SmallVector<Expr *, 4> elements; |
| SmallVector<Identifier, 4> elementNames; |
| SmallVector<SourceLoc, 4> elementLocs; |
| elements.push_back(initializer); |
| elementNames.push_back(ctx.Id_wrappedValue); |
| elementLocs.push_back(initializer->getStartLoc()); |
| |
| if (auto tuple = dyn_cast<TupleExpr>(attr->getArg())) { |
| for (unsigned i : range(tuple->getNumElements())) { |
| elements.push_back(tuple->getElement(i)); |
| elementNames.push_back(tuple->getElementName(i)); |
| elementLocs.push_back(tuple->getElementNameLoc(i)); |
| } |
| } else { |
| auto paren = cast<ParenExpr>(attr->getArg()); |
| elements.push_back(paren->getSubExpr()); |
| elementNames.push_back(Identifier()); |
| elementLocs.push_back(SourceLoc()); |
| } |
| |
| auto endLoc = attr->getArg()->getEndLoc(); |
| if (endLoc.isInvalid() && startLoc.isValid()) |
| endLoc = wrapperAttrs[i]->getTypeLoc().getSourceRange().End; |
| |
| initializer = CallExpr::create( |
| ctx, typeExpr, startLoc, elements, elementNames, elementLocs, |
| endLoc, nullptr, /*implicit=*/true); |
| } |
| |
| return initializer; |
| } |