| //===--- TypeCheckStorage.cpp - Checking Properties and Subscripts -------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 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 properties, subscripts as well |
| // as their accessors. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "CodeSynthesis.h" |
| #include "TypeChecker.h" |
| #include "TypeCheckAvailability.h" |
| #include "TypeCheckConcurrency.h" |
| #include "TypeCheckDecl.h" |
| #include "TypeCheckType.h" |
| #include "swift/AST/ASTContext.h" |
| #include "swift/AST/ASTWalker.h" |
| #include "swift/AST/DiagnosticsParse.h" |
| #include "swift/AST/DiagnosticsSema.h" |
| #include "swift/AST/Expr.h" |
| #include "swift/AST/GenericEnvironment.h" |
| #include "swift/AST/Initializer.h" |
| #include "swift/AST/ParameterList.h" |
| #include "swift/AST/Pattern.h" |
| #include "swift/AST/PropertyWrappers.h" |
| #include "swift/AST/ProtocolConformance.h" |
| #include "swift/AST/SourceFile.h" |
| #include "swift/AST/TypeCheckRequests.h" |
| #include "swift/AST/Types.h" |
| using namespace swift; |
| |
| /// Set each bound variable in the pattern to have an error type. |
| void swift::setBoundVarsTypeError(Pattern *pattern, ASTContext &ctx) { |
| pattern->forEachVariable([&](VarDecl *var) { |
| // Don't change the type of a variable that we've been able to |
| // compute a type for. |
| if (var->hasInterfaceType()) |
| return; |
| |
| var->setInvalid(); |
| }); |
| } |
| |
| /// Build a default initializer for the given type. |
| Expr *TypeChecker::buildDefaultInitializer(Type type) { |
| auto &Context = type->getASTContext(); |
| // Default-initialize optional types and weak values to 'nil'. |
| if (type->getReferenceStorageReferent()->getOptionalObjectType()) |
| return new (Context) NilLiteralExpr(SourceLoc(), /*Implicit=*/true); |
| |
| // Build tuple literals for tuple types. |
| if (auto tupleType = type->getAs<TupleType>()) { |
| SmallVector<Expr *, 2> inits; |
| for (const auto &elt : tupleType->getElements()) { |
| if (elt.isVararg()) |
| return nullptr; |
| |
| auto eltInit = TypeChecker::buildDefaultInitializer(elt.getType()); |
| if (!eltInit) |
| return nullptr; |
| |
| inits.push_back(eltInit); |
| } |
| |
| return TupleExpr::createImplicit(Context, inits, { }); |
| } |
| |
| // We don't default-initialize anything else. |
| return nullptr; |
| } |
| |
| /// Does the context allow pattern bindings that don't bind any variables? |
| static bool contextAllowsPatternBindingWithoutVariables(DeclContext *dc) { |
| |
| // Property decls in type context must bind variables. |
| if (dc->isTypeContext()) |
| return false; |
| |
| // Global variable decls must bind variables, except in scripts. |
| if (dc->isModuleScopeContext()) { |
| if (dc->getParentSourceFile() |
| && dc->getParentSourceFile()->isScriptMode()) |
| return true; |
| |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static bool hasStoredProperties(NominalTypeDecl *decl) { |
| return (isa<StructDecl>(decl) || |
| (isa<ClassDecl>(decl) && !decl->hasClangNode())); |
| } |
| |
| static void computeLoweredStoredProperties(NominalTypeDecl *decl) { |
| // Just walk over the members of the type, forcing backing storage |
| // for lazy properties and property wrappers to be synthesized. |
| for (auto *member : decl->getMembers()) { |
| auto *var = dyn_cast<VarDecl>(member); |
| if (!var || var->isStatic()) |
| continue; |
| |
| if (var->getAttrs().hasAttribute<LazyAttr>()) |
| (void) var->getLazyStorageProperty(); |
| |
| if (var->hasAttachedPropertyWrapper()) |
| (void) var->getPropertyWrapperBackingProperty(); |
| } |
| |
| // If this is an actor class, check conformance to the Actor protocol to |
| // ensure that the actor storage will get created (if needed). |
| if (auto classDecl = dyn_cast<ClassDecl>(decl)) { |
| if (classDecl->isActor()) { |
| ASTContext &ctx = decl->getASTContext(); |
| if (auto actorProto = ctx.getProtocol(KnownProtocolKind::Actor)) { |
| SmallVector<ProtocolConformance *, 1> conformances; |
| classDecl->lookupConformance( |
| decl->getModuleContext(), actorProto, conformances); |
| for (auto conformance : conformances) |
| TypeChecker::checkConformance(conformance->getRootNormalConformance()); |
| } |
| } |
| } |
| } |
| |
| ArrayRef<VarDecl *> |
| StoredPropertiesRequest::evaluate(Evaluator &evaluator, |
| NominalTypeDecl *decl) const { |
| if (!hasStoredProperties(decl)) |
| return ArrayRef<VarDecl *>(); |
| |
| SmallVector<VarDecl *, 4> results; |
| |
| // Unless we're in a source file we don't have to do anything |
| // special to lower lazy properties and property wrappers. |
| if (isa<SourceFile>(decl->getModuleScopeContext())) |
| computeLoweredStoredProperties(decl); |
| |
| for (auto *member : decl->getMembers()) { |
| if (auto *var = dyn_cast<VarDecl>(member)) |
| if (!var->isStatic() && var->hasStorage()) { |
| results.push_back(var); |
| } |
| } |
| |
| return decl->getASTContext().AllocateCopy(results); |
| } |
| |
| ArrayRef<Decl *> |
| StoredPropertiesAndMissingMembersRequest::evaluate(Evaluator &evaluator, |
| NominalTypeDecl *decl) const { |
| if (!hasStoredProperties(decl)) |
| return ArrayRef<Decl *>(); |
| |
| SmallVector<Decl *, 4> results; |
| |
| // Unless we're in a source file we don't have to do anything |
| // special to lower lazy properties and property wrappers. |
| if (isa<SourceFile>(decl->getModuleScopeContext())) |
| computeLoweredStoredProperties(decl); |
| |
| for (auto *member : decl->getMembers()) { |
| if (auto *var = dyn_cast<VarDecl>(member)) |
| if (!var->isStatic() && var->hasStorage()) |
| results.push_back(var); |
| |
| if (auto missing = dyn_cast<MissingMemberDecl>(member)) |
| if (missing->getNumberOfFieldOffsetVectorEntries() > 0) |
| results.push_back(missing); |
| } |
| |
| return decl->getASTContext().AllocateCopy(results); |
| } |
| |
| /// Validate the \c entryNumber'th entry in \c binding. |
| const PatternBindingEntry * |
| PatternBindingEntryRequest::evaluate(Evaluator &eval, |
| PatternBindingDecl *binding, |
| unsigned entryNumber) const { |
| const auto &pbe = binding->getPatternList()[entryNumber]; |
| auto &Context = binding->getASTContext(); |
| |
| // Resolve the pattern. |
| auto *pattern = TypeChecker::resolvePattern(binding->getPattern(entryNumber), |
| binding->getDeclContext(), |
| /*isStmtCondition*/ true); |
| if (!pattern) { |
| binding->setInvalid(); |
| binding->getPattern(entryNumber)->setType(ErrorType::get(Context)); |
| return &pbe; |
| } |
| |
| binding->setPattern(entryNumber, pattern, |
| binding->getInitContext(entryNumber)); |
| |
| // Validate 'static'/'class' on properties in nominal type decls. |
| auto StaticSpelling = binding->getStaticSpelling(); |
| if (StaticSpelling != StaticSpellingKind::None && |
| isa<ExtensionDecl>(binding->getDeclContext())) { |
| if (auto *NTD = binding->getDeclContext()->getSelfNominalTypeDecl()) { |
| if (!isa<ClassDecl>(NTD)) { |
| if (StaticSpelling == StaticSpellingKind::KeywordClass) { |
| binding->diagnose(diag::class_var_not_in_class, false) |
| .fixItReplace(binding->getStaticLoc(), "static"); |
| NTD->diagnose(diag::extended_type_declared_here); |
| } |
| } |
| } |
| } |
| |
| // Check the pattern. |
| auto contextualPattern = |
| ContextualPattern::forPatternBindingDecl(binding, entryNumber); |
| Type patternType = TypeChecker::typeCheckPattern(contextualPattern); |
| if (patternType->hasError()) { |
| swift::setBoundVarsTypeError(pattern, Context); |
| binding->setInvalid(); |
| pattern->setType(ErrorType::get(Context)); |
| return &pbe; |
| } |
| |
| // If we have a type but no initializer, check whether the type is |
| // default-initializable. If so, do it. |
| if (!pbe.isInitialized() && |
| binding->isDefaultInitializable(entryNumber) && |
| pattern->hasStorage()) { |
| if (auto defaultInit = TypeChecker::buildDefaultInitializer(patternType)) { |
| // If we got a default initializer, install it and re-type-check it |
| // to make sure it is properly coerced to the pattern type. |
| binding->setInit(entryNumber, defaultInit); |
| } |
| } |
| |
| // If the pattern contains some form of unresolved type, we'll need to |
| // check the initializer. |
| if (patternType->hasUnresolvedType() || |
| patternType->hasUnboundGenericType()) { |
| if (TypeChecker::typeCheckPatternBinding(binding, entryNumber, |
| patternType)) { |
| binding->setInvalid(); |
| return &pbe; |
| } |
| |
| // A pattern binding at top level is not allowed to pick up another decl's |
| // opaque result type as its type by type inference. |
| if (!binding->getDeclContext()->isLocalContext() && |
| binding->getInit(entryNumber)->getType()->hasOpaqueArchetype()) { |
| // TODO: Check whether the type is the pattern binding's own opaque type. |
| binding->diagnose(diag::inferred_opaque_type, |
| binding->getInit(entryNumber)->getType()); |
| } |
| } else { |
| // Coerce the pattern to the computed type. |
| if (auto newPattern = TypeChecker::coercePatternToType( |
| contextualPattern, patternType, |
| TypeResolverContext::PatternBindingDecl)) { |
| pattern = newPattern; |
| } else { |
| binding->setInvalid(); |
| pattern->setType(ErrorType::get(Context)); |
| return &pbe; |
| } |
| } |
| |
| // If the pattern binding appears in a type or library file context, then |
| // it must bind at least one variable. |
| if (!contextAllowsPatternBindingWithoutVariables(binding->getDeclContext())) { |
| llvm::SmallVector<VarDecl *, 2> vars; |
| binding->getPattern(entryNumber)->collectVariables(vars); |
| if (vars.empty()) { |
| // Selector for error message. |
| enum : unsigned { |
| Property, |
| GlobalVariable, |
| }; |
| Context.Diags.diagnose(binding->getPattern(entryNumber)->getLoc(), |
| diag::pattern_binds_no_variables, |
| binding->getDeclContext()->isTypeContext() |
| ? Property |
| : GlobalVariable); |
| } |
| } |
| return &pbe; |
| } |
| |
| bool |
| IsGetterMutatingRequest::evaluate(Evaluator &evaluator, |
| AbstractStorageDecl *storage) const { |
| auto storageDC = storage->getDeclContext(); |
| bool result = (!storage->isStatic() && storageDC->isTypeContext() && |
| storageDC->hasValueSemantics()); |
| |
| // 'lazy' overrides the normal accessor-based rules and heavily |
| // restricts what accessors can be used. The getter is considered |
| // mutating if this is instance storage on a value type. |
| if (storage->getAttrs().hasAttribute<LazyAttr>()) { |
| return result; |
| } |
| |
| // If we have an attached property wrapper, the getter's mutating-ness |
| // depends on the composition of the wrappers. |
| if (auto var = dyn_cast<VarDecl>(storage)) { |
| if (auto mut = var->getPropertyWrapperMutability()) { |
| return mut->Getter == PropertyWrapperMutability::Mutating |
| && result; |
| } |
| } |
| |
| auto checkMutability = [&](AccessorKind kind) -> bool { |
| auto *accessor = storage->getParsedAccessor(kind); |
| if (!accessor) |
| return false; |
| |
| return accessor->isMutating(); |
| }; |
| |
| // Protocol requirements are always written as '{ get }' or '{ get set }'; |
| // the @_borrowed attribute determines if getReadImpl() becomes Get or Read. |
| if (isa<ProtocolDecl>(storageDC)) |
| return checkMutability(AccessorKind::Get); |
| |
| switch (storage->getReadImpl()) { |
| case ReadImplKind::Stored: |
| case ReadImplKind::Inherited: |
| return false; |
| |
| case ReadImplKind::Get: |
| return checkMutability(AccessorKind::Get); |
| |
| case ReadImplKind::Address: |
| return checkMutability(AccessorKind::Address); |
| |
| case ReadImplKind::Read: |
| return checkMutability(AccessorKind::Read); |
| } |
| |
| llvm_unreachable("bad impl kind"); |
| } |
| |
| bool |
| IsSetterMutatingRequest::evaluate(Evaluator &evaluator, |
| AbstractStorageDecl *storage) const { |
| // By default, the setter is mutating if we have an instance member of a |
| // value type, but this can be overridden below. |
| auto storageDC = storage->getDeclContext(); |
| bool result = (!storage->isStatic() && storageDC->isTypeContext() && |
| storageDC->hasValueSemantics()); |
| |
| // If we have an attached property wrapper, the setter is mutating |
| // or not based on the composition of the wrappers. |
| if (auto var = dyn_cast<VarDecl>(storage)) { |
| if (auto mut = var->getPropertyWrapperMutability()) { |
| bool isMutating = mut->Setter == PropertyWrapperMutability::Mutating; |
| if (auto *didSet = var->getParsedAccessor(AccessorKind::DidSet)) { |
| // If there's a didSet, we call the getter for the 'oldValue', and so |
| // should consider the getter's mutatingness as well |
| isMutating |= (mut->Getter == PropertyWrapperMutability::Mutating); |
| isMutating |= didSet->getAttrs().hasAttribute<MutatingAttr>(); |
| } |
| if (auto *willSet = var->getParsedAccessor(AccessorKind::WillSet)) |
| isMutating |= willSet->getAttrs().hasAttribute<MutatingAttr>(); |
| return isMutating && result; |
| } |
| } |
| |
| auto impl = storage->getImplInfo(); |
| switch (impl.getWriteImpl()) { |
| case WriteImplKind::Immutable: |
| case WriteImplKind::Stored: |
| // Instance member setters are mutating; static property setters and |
| // top-level setters are not. |
| // It's important that we use this logic for "immutable" storage |
| // in order to handle initialization of let-properties. |
| return result; |
| |
| case WriteImplKind::StoredWithObservers: |
| case WriteImplKind::InheritedWithObservers: |
| case WriteImplKind::Set: { |
| auto *setter = storage->getParsedAccessor(AccessorKind::Set); |
| |
| if (setter) |
| result = setter->isMutating(); |
| |
| |
| // As a special extra check, if the user also gave us a modify |
| // coroutine, check that it has the same mutatingness as the setter. |
| // TODO: arguably this should require the spelling to match even when |
| // it's the implied value. |
| auto modifyAccessor = storage->getParsedAccessor(AccessorKind::Modify); |
| |
| if (impl.getReadWriteImpl() == ReadWriteImplKind::Modify && |
| modifyAccessor != nullptr) { |
| auto modifyResult = modifyAccessor->isMutating(); |
| if ((result || storage->isGetterMutating()) != modifyResult) { |
| modifyAccessor->diagnose( |
| diag::modify_mutatingness_differs_from_setter, |
| modifyResult ? SelfAccessKind::Mutating |
| : SelfAccessKind::NonMutating, |
| modifyResult ? SelfAccessKind::NonMutating |
| : SelfAccessKind::Mutating); |
| if (setter) |
| setter->diagnose(diag::previous_accessor, "setter", 0); |
| modifyAccessor->setInvalid(); |
| } |
| } |
| |
| return result; |
| } |
| |
| case WriteImplKind::MutableAddress: |
| return storage->getParsedAccessor(AccessorKind::MutableAddress) |
| ->isMutating(); |
| |
| case WriteImplKind::Modify: |
| return storage->getParsedAccessor(AccessorKind::Modify) |
| ->isMutating(); |
| } |
| llvm_unreachable("bad storage kind"); |
| } |
| |
| OpaqueReadOwnership |
| OpaqueReadOwnershipRequest::evaluate(Evaluator &evaluator, |
| AbstractStorageDecl *storage) const { |
| return (storage->getAttrs().hasAttribute<BorrowedAttr>() |
| ? OpaqueReadOwnership::Borrowed |
| : OpaqueReadOwnership::Owned); |
| } |
| |
| /// Insert the specified decl into the DeclContext's member list. If the hint |
| /// decl is specified, the new decl is inserted next to the hint. |
| static void addMemberToContextIfNeeded(Decl *D, DeclContext *DC, |
| Decl *Hint = nullptr) { |
| if (auto *ntd = dyn_cast<NominalTypeDecl>(DC)) { |
| ntd->addMember(D, Hint); |
| } else if (auto *ed = dyn_cast<ExtensionDecl>(DC)) { |
| ed->addMember(D, Hint); |
| } else { |
| assert((DC->isLocalContext() || isa<FileUnit>(DC)) && |
| "Unknown declcontext"); |
| } |
| } |
| |
| /// Build a parameter list which can forward the formal index parameters of a |
| /// declaration. |
| /// |
| /// \param prefix optional arguments to be prefixed onto the index |
| /// forwarding pattern. |
| static ParameterList * |
| buildIndexForwardingParamList(AbstractStorageDecl *storage, |
| ArrayRef<ParamDecl*> prefix, |
| ASTContext &context) { |
| auto subscript = dyn_cast<SubscriptDecl>(storage); |
| |
| // Fast path: if this isn't a subscript, just use whatever we have. |
| if (!subscript) |
| return ParameterList::create(context, prefix); |
| |
| // Clone the parameter list over for a new decl, so we get new ParamDecls. |
| auto indices = subscript->getIndices()->clone(context, |
| ParameterList::Implicit); |
| |
| // Give all of the parameters meaningless names so that we can forward |
| // them properly. If it's declared anonymously, SILGen will think |
| // it's unused. |
| // TODO: use some special DeclBaseName for this? |
| for (auto param : indices->getArray()) { |
| if (!param->hasName()) |
| param->setName(context.getIdentifier("anonymous")); |
| assert(param->hasName()); |
| } |
| |
| if (prefix.empty()) |
| return indices; |
| |
| |
| // Otherwise, we need to build up a new parameter list. |
| SmallVector<ParamDecl*, 4> elements; |
| |
| // Start with the fields we were given, if there are any. |
| elements.append(prefix.begin(), prefix.end()); |
| elements.append(indices->begin(), indices->end()); |
| return ParameterList::create(context, elements); |
| } |
| |
| /// Create the generic parameters needed for the given accessor, if any. |
| static GenericParamList *createAccessorGenericParams( |
| AbstractStorageDecl *storage) { |
| // Accessors of generic subscripts get a copy of the subscript's |
| // generic parameter list, because they're not nested inside the |
| // subscript. |
| if (auto *subscript = dyn_cast<SubscriptDecl>(storage)) { |
| if (auto genericParams = subscript->getGenericParams()) |
| return genericParams->clone(subscript->getDeclContext()); |
| } |
| |
| return nullptr; |
| } |
| |
| static bool doesAccessorHaveBody(AccessorDecl *accessor) { |
| // Protocol requirements don't have bodies. |
| // |
| // FIXME: Revisit this if we ever get 'real' default implementations. |
| if (isa<ProtocolDecl>(accessor->getDeclContext())) |
| return false; |
| |
| auto *storage = accessor->getStorage(); |
| |
| // NSManaged getters and setters don't have bodies. |
| if (storage->getAttrs().hasAttribute<NSManagedAttr>()) |
| if (accessor->isGetterOrSetter()) |
| return false; |
| |
| return true; |
| } |
| |
| |
| /// Build a reference to the subscript index variables for this subscript |
| /// accessor. |
| static Expr *buildSubscriptIndexReference(ASTContext &ctx, |
| AccessorDecl *accessor) { |
| // Pull out the body parameters, which we should have cloned |
| // previously to be forwardable. Drop the initial buffer/value |
| // parameter in accessors that have one. |
| auto params = accessor->getParameters()->getArray(); |
| auto accessorKind = accessor->getAccessorKind(); |
| |
| // Ignore the value parameter of a setter. |
| if (accessorKind == AccessorKind::Set) { |
| params = params.slice(1); |
| } |
| |
| // Okay, everything else should be forwarded, build the expression. |
| return buildArgumentForwardingExpr(params, ctx); |
| } |
| |
| namespace { |
| enum class TargetImpl { |
| /// We're doing an ordinary storage reference. |
| Ordinary, |
| /// We're referencing the physical storage created for the storage. |
| Storage, |
| /// We're referencing this specific implementation of the storage, not |
| /// an override of it. |
| Implementation, |
| /// We're referencing the superclass's implementation of the storage. |
| Super, |
| /// We're referencing the backing property for a property with a wrapper |
| /// through the 'value' property. |
| Wrapper, |
| /// We're referencing the backing property for a property with a wrapper |
| /// through the 'projectedValue' property. |
| WrapperStorage, |
| }; |
| } // end anonymous namespace |
| |
| namespace { |
| /// Describes the information needed to perform property wrapper access via |
| /// the enclosing self. |
| struct EnclosingSelfPropertyWrapperAccess { |
| /// The (genreric) subscript that will be used to perform the access. |
| SubscriptDecl *subscript; |
| |
| /// The property being accessed. |
| VarDecl *accessedProperty; |
| }; |
| } |
| |
| /// Determine whether the given property should be accessed via the enclosing-self access pattern. |
| static Optional<EnclosingSelfPropertyWrapperAccess> |
| getEnclosingSelfPropertyWrapperAccess(VarDecl *property, bool forProjected) { |
| // The enclosing-self pattern only applies to instance properties of |
| // classes. |
| if (!property->isInstanceMember()) |
| return None; |
| auto classDecl = property->getDeclContext()->getSelfClassDecl(); |
| if (!classDecl) |
| return None; |
| |
| // The pattern currently only works with the outermost property wrapper. |
| Type outermostWrapperType = property->getPropertyWrapperBackingPropertyType(); |
| if (!outermostWrapperType) |
| return None; |
| NominalTypeDecl *wrapperTypeDecl = outermostWrapperType->getAnyNominal(); |
| if (!wrapperTypeDecl) |
| return None; |
| |
| // Look for a generic subscript that fits the general form we need. |
| auto wrapperInfo = wrapperTypeDecl->getPropertyWrapperTypeInfo(); |
| auto subscript = |
| forProjected ? wrapperInfo.enclosingInstanceProjectedSubscript |
| : wrapperInfo.enclosingInstanceWrappedSubscript; |
| if (!subscript) |
| return None; |
| |
| EnclosingSelfPropertyWrapperAccess result; |
| result.subscript = subscript; |
| |
| if (forProjected) { |
| result.accessedProperty = |
| property->getPropertyWrapperBackingPropertyInfo().projectionVar; |
| } else { |
| result.accessedProperty = property; |
| } |
| return result; |
| } |
| |
| static Optional<PropertyWrapperLValueness> |
| getPropertyWrapperLValueness(VarDecl *var) { |
| auto &ctx = var->getASTContext(); |
| return evaluateOrDefault( |
| ctx.evaluator, |
| PropertyWrapperLValuenessRequest{var}, |
| None); |
| } |
| |
| /// Build a reference to the storage of a declaration. Returns nullptr if there |
| /// was an error. This should only occur if an invalid declaration was type |
| /// checked; another diagnostic should have been emitted already. |
| /// |
| /// The resulting reference is used in synthesized property accessors and is of |
| /// one of the following forms: |
| /// 1. Without property wrappers: |
| /// - Stored: \c self.member |
| /// 2. With property wrappers: |
| /// - Wrapped: \c self._member.wrappedValue |
| /// - Composition: \c self._member.wrappedValue.wrappedValue….wrappedValue |
| /// - Projected: \c self._member.projectedValue |
| /// - Enclosed instance: \c Wrapper[_enclosedInstance: self, …] |
| static Expr *buildStorageReference(AccessorDecl *accessor, |
| AbstractStorageDecl *storage, |
| TargetImpl target, |
| bool isUsedForGetAccess, |
| bool isUsedForSetAccess, |
| ASTContext &ctx) { |
| // Whether the last component of the expression should be an l-value |
| bool isLValue = isUsedForSetAccess; |
| // Local function to "finish" the expression, creating a member reference |
| // to the given sequence of underlying variables. |
| Optional<EnclosingSelfPropertyWrapperAccess> enclosingSelfAccess; |
| // Contains the underlying wrappedValue declaration in a property wrapper |
| // along with whether or not the reference to this field needs to be an lvalue |
| llvm::SmallVector<std::pair<VarDecl *, bool>, 1> underlyingVars; |
| auto finish = [&](Expr *result) -> Expr * { |
| for (auto underlyingVarPair : underlyingVars) { |
| auto underlyingVar = underlyingVarPair.first; |
| auto isWrapperRefLValue = underlyingVarPair.second; |
| auto subs = result->getType() |
| ->getWithoutSpecifierType() |
| ->getContextSubstitutionMap( |
| accessor->getParentModule(), |
| underlyingVar->getDeclContext()); |
| |
| ConcreteDeclRef memberRef(underlyingVar, subs); |
| auto *memberRefExpr = new (ctx) MemberRefExpr( |
| result, SourceLoc(), memberRef, DeclNameLoc(), /*Implicit=*/true); |
| auto type = underlyingVar->getValueInterfaceType().subst(subs); |
| if (isWrapperRefLValue) |
| type = LValueType::get(type); |
| memberRefExpr->setType(type); |
| |
| result = memberRefExpr; |
| } |
| |
| return result; |
| }; |
| |
| VarDecl *selfDecl = accessor->getImplicitSelfDecl(); |
| |
| AccessSemantics semantics; |
| SelfAccessorKind selfAccessKind; |
| Type selfTypeForAccess = (selfDecl ? selfDecl->getType() : Type()); |
| |
| bool isMemberLValue = isLValue; |
| |
| auto *genericEnv = accessor->getGenericEnvironment(); |
| SubstitutionMap subs; |
| if (genericEnv) |
| subs = genericEnv->getForwardingSubstitutionMap(); |
| |
| switch (target) { |
| case TargetImpl::Ordinary: |
| semantics = AccessSemantics::Ordinary; |
| selfAccessKind = SelfAccessorKind::Peer; |
| break; |
| |
| case TargetImpl::Storage: |
| semantics = AccessSemantics::DirectToStorage; |
| selfAccessKind = SelfAccessorKind::Peer; |
| break; |
| |
| case TargetImpl::Implementation: |
| semantics = AccessSemantics::DirectToImplementation; |
| selfAccessKind = SelfAccessorKind::Peer; |
| break; |
| |
| case TargetImpl::Super: |
| // If this really is an override, use a super-access. |
| if (auto override = storage->getOverriddenDecl()) { |
| semantics = AccessSemantics::Ordinary; |
| selfAccessKind = SelfAccessorKind::Super; |
| |
| auto isMetatype = false; |
| if (auto *metaTy = selfTypeForAccess->getAs<MetatypeType>()) { |
| isMetatype = true; |
| selfTypeForAccess = metaTy->getInstanceType(); |
| } |
| |
| // Adjust the self type of the access to refer to the relevant superclass. |
| auto *baseClass = override->getDeclContext()->getSelfClassDecl(); |
| selfTypeForAccess = selfTypeForAccess->getSuperclassForDecl(baseClass); |
| subs = |
| selfTypeForAccess->getContextSubstitutionMap( |
| accessor->getParentModule(), |
| baseClass); |
| |
| storage = override; |
| |
| if (isMetatype) |
| selfTypeForAccess = MetatypeType::get(selfTypeForAccess); |
| |
| // Otherwise do a self-reference, which is dynamically bogus but |
| // should be statically valid. This should only happen in invalid cases. |
| } else { |
| semantics = AccessSemantics::Ordinary; |
| selfAccessKind = SelfAccessorKind::Peer; |
| } |
| break; |
| |
| case TargetImpl::Wrapper: { |
| auto var = cast<VarDecl>(accessor->getStorage()); |
| auto *backing = var->getPropertyWrapperBackingProperty(); |
| |
| // Error recovery. |
| if (!backing || backing->isInvalid()) |
| return nullptr; |
| |
| storage = backing; |
| |
| // If the outermost property wrapper uses the enclosing self pattern, |
| // record that. |
| unsigned lastWrapperIdx = var->getAttachedPropertyWrappers().size(); |
| unsigned firstWrapperIdx = 0; |
| enclosingSelfAccess = |
| getEnclosingSelfPropertyWrapperAccess(var, /*forProjected=*/false); |
| if (enclosingSelfAccess) |
| firstWrapperIdx = 1; |
| |
| // Perform accesses to the wrappedValues along the composition chain. |
| if (firstWrapperIdx < lastWrapperIdx) { |
| auto lvalueness = *getPropertyWrapperLValueness(var); |
| |
| // Figure out if the outermost wrapper instance should be an l-value |
| bool isLValueForGet = lvalueness.isLValueForGetAccess[firstWrapperIdx]; |
| bool isLValueForSet = lvalueness.isLValueForSetAccess[firstWrapperIdx]; |
| isMemberLValue = (isLValueForGet && isUsedForGetAccess) || |
| (isLValueForSet && isUsedForSetAccess); |
| |
| for (unsigned i : range(firstWrapperIdx, lastWrapperIdx)) { |
| auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(i); |
| auto wrappedValue = wrapperInfo.valueVar; |
| |
| // Figure out if the wrappedValue accesses should be l-values |
| bool isWrapperRefLValue = isLValue; |
| if (i < lastWrapperIdx - 1) { |
| bool isLValueForGet = lvalueness.isLValueForGetAccess[i+1]; |
| bool isLValueForSet = lvalueness.isLValueForSetAccess[i+1]; |
| isWrapperRefLValue = (isLValueForGet && isUsedForGetAccess) || |
| (isLValueForSet && isUsedForSetAccess); |
| } |
| |
| // Check for availability of wrappedValue. |
| if (accessor->getAccessorKind() == AccessorKind::Get || |
| accessor->getAccessorKind() == AccessorKind::Read) { |
| if (wrappedValue->getAttrs().getUnavailable(ctx)) { |
| ExportContext where = ExportContext::forDeclSignature(var); |
| diagnoseExplicitUnavailability( |
| wrappedValue, |
| var->getAttachedPropertyWrappers()[i]->getRangeWithAt(), |
| where, nullptr); |
| } |
| } |
| |
| underlyingVars.push_back({ wrappedValue, isWrapperRefLValue }); |
| } |
| } |
| semantics = AccessSemantics::DirectToStorage; |
| selfAccessKind = SelfAccessorKind::Peer; |
| break; |
| } |
| |
| case TargetImpl::WrapperStorage: { |
| auto var = |
| cast<VarDecl>(accessor->getStorage())->getOriginalWrappedProperty(); |
| auto *backing = var->getPropertyWrapperBackingProperty(); |
| |
| // Error recovery. |
| if (!backing || backing->isInvalid()) |
| return nullptr; |
| |
| storage = backing; |
| |
| enclosingSelfAccess = |
| getEnclosingSelfPropertyWrapperAccess(var, /*forProjected=*/true); |
| if (!enclosingSelfAccess) { |
| auto projectionVar = cast<VarDecl>(accessor->getStorage()); |
| if (auto lvalueness = getPropertyWrapperLValueness(projectionVar)) { |
| isMemberLValue = |
| (lvalueness->isLValueForGetAccess[0] && isUsedForGetAccess) || |
| (lvalueness->isLValueForSetAccess[0] && isUsedForSetAccess); |
| } |
| underlyingVars.push_back( |
| { var->getAttachedPropertyWrapperTypeInfo(0).projectedValueVar, |
| isLValue }); |
| } |
| semantics = AccessSemantics::DirectToStorage; |
| selfAccessKind = SelfAccessorKind::Peer; |
| break; |
| } |
| } |
| |
| // If the base is not 'self', default get access to nonmutating and set access to mutating. |
| bool getterMutatesBase = selfDecl && storage->isGetterMutating(); |
| bool setterMutatesBase = !selfDecl || storage->isSetterMutating(); |
| // If we're not accessing via a property wrapper, we don't need to adjust |
| // the mutability. |
| if (target == TargetImpl::Wrapper || target == TargetImpl::WrapperStorage) { |
| auto var = cast<VarDecl>(accessor->getStorage()); |
| auto mutability = var->getPropertyWrapperMutability(); |
| // Only adjust mutability if it's possible to mutate the base. |
| if (mutability && !var->isStatic() && |
| !(selfDecl && selfTypeForAccess->hasReferenceSemantics())) { |
| getterMutatesBase = (mutability->Getter == PropertyWrapperMutability::Mutating); |
| setterMutatesBase = (mutability->Setter == PropertyWrapperMutability::Mutating); |
| } |
| } |
| |
| // If the accessor is mutating, then the base should be referred as an l-value |
| bool isBaseLValue = (getterMutatesBase && isUsedForGetAccess) || |
| (setterMutatesBase && isUsedForSetAccess); |
| |
| if (!selfDecl) { |
| assert(target != TargetImpl::Super); |
| auto *storageDRE = new (ctx) DeclRefExpr(storage, DeclNameLoc(), |
| /*IsImplicit=*/true, semantics); |
| auto type = storage->getValueInterfaceType().subst(subs); |
| if (isBaseLValue) |
| type = LValueType::get(type); |
| storageDRE->setType(type); |
| |
| return finish(storageDRE); |
| } |
| |
| // Build self |
| Expr *selfDRE = buildSelfReference(selfDecl, selfAccessKind, isBaseLValue, |
| /*convertTy*/ selfTypeForAccess); |
| if (isBaseLValue) |
| selfTypeForAccess = LValueType::get(selfTypeForAccess); |
| |
| if (!selfDRE->getType()->isEqual(selfTypeForAccess)) { |
| assert(selfAccessKind == SelfAccessorKind::Super); |
| selfDRE = new (ctx) DerivedToBaseExpr(selfDRE, selfTypeForAccess); |
| } |
| |
| // Build self.member or equivalent |
| |
| Expr *lookupExpr; |
| ConcreteDeclRef memberRef(storage, subs); |
| auto type = storage->getValueInterfaceType().subst(subs); |
| if (isMemberLValue) |
| type = LValueType::get(type); |
| |
| // When we are performing access via a property wrapper's static subscript |
| // that accepts the enclosing self along with key paths, form that subscript |
| // operation now. |
| if (enclosingSelfAccess) { |
| Type storageType = storage->getValueInterfaceType().subst(subs); |
| // Metatype instance for the wrapper type itself. |
| TypeExpr *wrapperMetatype = TypeExpr::createImplicit(storageType, ctx); |
| |
| // Key path referring to the property being accessed. |
| Expr *propertyKeyPath = new (ctx) KeyPathDotExpr(SourceLoc()); |
| propertyKeyPath = UnresolvedDotExpr::createImplicit(ctx, propertyKeyPath, |
| enclosingSelfAccess->accessedProperty->getName()); |
| propertyKeyPath = new (ctx) KeyPathExpr( |
| SourceLoc(), nullptr, propertyKeyPath, /*hasLeadingDot=*/true); |
| |
| // Key path referring to the backing storage property. |
| Expr *storageKeyPath = new (ctx) KeyPathDotExpr(SourceLoc()); |
| storageKeyPath = UnresolvedDotExpr::createImplicit(ctx, storageKeyPath, |
| storage->getName()); |
| storageKeyPath = new (ctx) KeyPathExpr(SourceLoc(), nullptr, storageKeyPath, |
| /*hasLeadingDot=*/true); |
| Expr *args[3] = {selfDRE, propertyKeyPath, storageKeyPath}; |
| |
| SubscriptDecl *subscriptDecl = enclosingSelfAccess->subscript; |
| lookupExpr = SubscriptExpr::create( |
| ctx, wrapperMetatype, SourceLoc(), args, |
| subscriptDecl->getName().getArgumentNames(), { }, SourceLoc(), |
| /*trailingClosures=*/{}, subscriptDecl, /*Implicit=*/true); |
| |
| // FIXME: Since we're not resolving overloads or anything, we should be |
| // building fully type-checked AST above; we already have all the |
| // information that we need. |
| if (!TypeChecker::typeCheckExpression(lookupExpr, accessor)) |
| return nullptr; |
| |
| // Make sure we produce an lvalue only when desired. |
| if (isMemberLValue != lookupExpr->getType()->is<LValueType>()) { |
| if (isMemberLValue) { |
| // Strip off an extraneous load. |
| if (auto load = dyn_cast<LoadExpr>(lookupExpr)) |
| lookupExpr = load->getSubExpr(); |
| } else { |
| lookupExpr = new (ctx) LoadExpr( |
| lookupExpr, lookupExpr->getType()->getRValueType()); |
| } |
| } |
| } else if (isa<SubscriptDecl>(storage)) { |
| Expr *indices = buildSubscriptIndexReference(ctx, accessor); |
| lookupExpr = SubscriptExpr::create(ctx, selfDRE, indices, memberRef, |
| /*IsImplicit=*/true, semantics); |
| |
| if (selfAccessKind == SelfAccessorKind::Super) |
| cast<LookupExpr>(lookupExpr)->setIsSuper(true); |
| |
| lookupExpr->setType(type); |
| |
| } else { |
| lookupExpr = new (ctx) MemberRefExpr(selfDRE, SourceLoc(), memberRef, |
| DeclNameLoc(), /*IsImplicit=*/true, |
| semantics); |
| |
| if (selfAccessKind == SelfAccessorKind::Super) |
| cast<LookupExpr>(lookupExpr)->setIsSuper(true); |
| |
| lookupExpr->setType(type); |
| } |
| |
| // Build self.member.wrappedValue if applicable |
| |
| return finish(lookupExpr); |
| } |
| |
| /// Load the value of VD. If VD is an @override of another value, we call the |
| /// superclass getter. Otherwise, we do a direct load of the value. |
| static Expr * |
| createPropertyLoadOrCallSuperclassGetter(AccessorDecl *accessor, |
| AbstractStorageDecl *storage, |
| TargetImpl target, |
| ASTContext &ctx) { |
| return buildStorageReference(accessor, storage, target, |
| /*isUsedForGetAccess=*/true, |
| /*isUsedForSetAccess=*/false, |
| ctx); |
| } |
| |
| static ProtocolConformanceRef checkConformanceToNSCopying(VarDecl *var, |
| Type type) { |
| auto dc = var->getDeclContext(); |
| auto &ctx = dc->getASTContext(); |
| auto proto = ctx.getNSCopyingDecl(); |
| |
| if (proto) { |
| if (auto result = TypeChecker::conformsToProtocol(type, proto, dc)) |
| return result; |
| } |
| |
| ctx.Diags.diagnose(var->getLoc(), diag::nscopying_doesnt_conform); |
| return ProtocolConformanceRef::forInvalid(); |
| } |
| |
| static std::pair<Type, bool> getUnderlyingTypeOfVariable(VarDecl *var) { |
| Type type = var->getType()->getReferenceStorageReferent(); |
| |
| if (Type objectType = type->getOptionalObjectType()) { |
| return {objectType, true}; |
| } else { |
| return {type, false}; |
| } |
| } |
| |
| ProtocolConformanceRef TypeChecker::checkConformanceToNSCopying(VarDecl *var) { |
| Type type = getUnderlyingTypeOfVariable(var).first; |
| return ::checkConformanceToNSCopying(var, type); |
| } |
| |
| /// Synthesize the code to store 'Val' to 'VD', given that VD has an @NSCopying |
| /// attribute on it. We know that VD is a stored property in a class, so we |
| /// just need to generate something like "self.property = val.copy(zone: nil)" |
| /// here. This does some type checking to validate that the call will succeed. |
| static Expr *synthesizeCopyWithZoneCall(Expr *Val, VarDecl *VD, |
| ASTContext &Ctx) { |
| // We support @NSCopying on class types (which conform to NSCopying), |
| // protocols which conform, and option types thereof. |
| auto underlyingTypeAndIsOptional = getUnderlyingTypeOfVariable(VD); |
| auto underlyingType = underlyingTypeAndIsOptional.first; |
| auto isOptional = underlyingTypeAndIsOptional.second; |
| |
| // The element type must conform to NSCopying. If not, emit an error and just |
| // recovery by synthesizing without the copy call. |
| auto conformance = checkConformanceToNSCopying(VD, underlyingType); |
| if (!conformance) |
| return Val; |
| |
| //- (id)copyWithZone:(NSZone *)zone; |
| DeclName copyWithZoneName(Ctx, Ctx.getIdentifier("copy"), { Ctx.Id_with }); |
| FuncDecl *copyMethod = nullptr; |
| for (auto member : conformance.getRequirement()->getMembers()) { |
| if (auto func = dyn_cast<FuncDecl>(member)) { |
| if (func->getName() == copyWithZoneName) { |
| copyMethod = func; |
| break; |
| } |
| } |
| } |
| assert(copyMethod != nullptr); |
| |
| // If we have an optional type, we have to "?" the incoming value to only |
| // evaluate the subexpression if the incoming value is non-null. |
| if (isOptional) { |
| Val = new (Ctx) BindOptionalExpr(Val, SourceLoc(), 0); |
| Val->setType(underlyingType); |
| } |
| |
| SubstitutionMap subs = |
| SubstitutionMap::get(copyMethod->getGenericSignature(), {underlyingType}, |
| ArrayRef<ProtocolConformanceRef>(conformance)); |
| ConcreteDeclRef copyMethodRef(copyMethod, subs); |
| auto copyMethodType = copyMethod->getInterfaceType() |
| ->castTo<GenericFunctionType>() |
| ->substGenericArgs(subs); |
| auto DRE = new (Ctx) DeclRefExpr(copyMethodRef, DeclNameLoc(), |
| /*IsImplicit=*/true); |
| DRE->setType(copyMethodType); |
| |
| // Drop the self type |
| copyMethodType = copyMethodType->getResult()->castTo<FunctionType>(); |
| |
| auto DSCE = new (Ctx) DotSyntaxCallExpr(DRE, SourceLoc(), Val); |
| DSCE->setImplicit(); |
| DSCE->setType(copyMethodType); |
| DSCE->setThrows(false); |
| |
| Expr *Nil = new (Ctx) NilLiteralExpr(SourceLoc(), /*implicit*/true); |
| Nil->setType(copyMethodType->getParams()[0].getParameterType()); |
| |
| auto *Call = CallExpr::createImplicit(Ctx, DSCE, { Nil }, { Ctx.Id_with }); |
| Call->setType(copyMethodType->getResult()); |
| Call->setThrows(false); |
| |
| // If we're working with non-optional types, we're forcing the cast. |
| if (!isOptional) { |
| auto *const Cast = |
| ForcedCheckedCastExpr::createImplicit(Ctx, Call, underlyingType); |
| Cast->setCastKind(CheckedCastKind::ValueCast); |
| |
| return Cast; |
| } |
| |
| // We're working with optional types, so perform a conditional checked |
| // downcast. |
| auto *const Cast = |
| ConditionalCheckedCastExpr::createImplicit(Ctx, Call, underlyingType); |
| Cast->setCastKind(CheckedCastKind::ValueCast); |
| |
| // Use OptionalEvaluationExpr to evaluate the "?". |
| auto *Result = new (Ctx) OptionalEvaluationExpr(Cast); |
| Result->setType(OptionalType::get(underlyingType)); |
| |
| return Result; |
| } |
| |
| /// In a synthesized accessor body, store 'value' to the appropriate element. |
| /// |
| /// If the property is an override, we call the superclass setter. |
| /// Otherwise, we do a direct store of the value. |
| static |
| void createPropertyStoreOrCallSuperclassSetter(AccessorDecl *accessor, |
| Expr *value, |
| AbstractStorageDecl *storage, |
| TargetImpl target, |
| SmallVectorImpl<ASTNode> &body, |
| ASTContext &ctx) { |
| // If the storage is an @NSCopying property, then we store the |
| // result of a copyWithZone call on the value, not the value itself. |
| if (auto property = dyn_cast<VarDecl>(storage)) { |
| if (property->getAttrs().hasAttribute<NSCopyingAttr>()) |
| value = synthesizeCopyWithZoneCall(value, property, ctx); |
| } |
| |
| // Error recovery. |
| if (value->getType()->hasError()) |
| return; |
| |
| Expr *dest = buildStorageReference(accessor, storage, target, |
| /*isUsedForGetAccess=*/false, |
| /*isUsedForSetAccess=*/true, |
| ctx); |
| |
| // Error recovery. |
| if (dest == nullptr) |
| return; |
| |
| // A lazy property setter will store a value of type T into underlying storage |
| // of type T?. |
| auto destType = dest->getType()->getWithoutSpecifierType(); |
| |
| // Error recovery. |
| if (destType->hasError()) |
| return; |
| |
| if (!destType->isEqual(value->getType())) { |
| assert(destType->getOptionalObjectType()); |
| assert(destType->getOptionalObjectType()->isEqual(value->getType())); |
| value = new (ctx) InjectIntoOptionalExpr(value, destType); |
| } |
| |
| auto *assign = new (ctx) AssignExpr(dest, SourceLoc(), value, |
| /*IsImplicit=*/true); |
| assign->setType(ctx.TheEmptyTupleType); |
| |
| body.push_back(assign); |
| } |
| |
| LLVM_ATTRIBUTE_UNUSED |
| static bool isSynthesizedComputedProperty(AbstractStorageDecl *storage) { |
| return (storage->getAttrs().hasAttribute<LazyAttr>() || |
| storage->getAttrs().hasAttribute<NSManagedAttr>() || |
| (isa<VarDecl>(storage) && |
| cast<VarDecl>(storage)->hasAttachedPropertyWrapper())); |
| } |
| |
| /// Synthesize the body of a trivial getter. For a non-member vardecl or one |
| /// which is not an override of a base class property, it performs a direct |
| /// storage load. For an override of a base member property, it chains up to |
| /// super. |
| static std::pair<BraceStmt *, bool> |
| synthesizeTrivialGetterBody(AccessorDecl *getter, TargetImpl target, |
| ASTContext &ctx) { |
| auto storage = getter->getStorage(); |
| assert(!isSynthesizedComputedProperty(storage) || |
| target == TargetImpl::Wrapper || |
| target == TargetImpl::WrapperStorage); |
| |
| SourceLoc loc = storage->getLoc(); |
| |
| Expr *result = |
| createPropertyLoadOrCallSuperclassGetter(getter, storage, target, ctx); |
| |
| SmallVector<ASTNode, 2> body; |
| if (result != nullptr) { |
| ASTNode returnStmt = new (ctx) ReturnStmt(SourceLoc(), result, |
| /*IsImplicit=*/true); |
| body.push_back(returnStmt); |
| } |
| |
| return { BraceStmt::create(ctx, loc, body, loc, true), |
| /*isTypeChecked=*/true }; |
| } |
| |
| /// Synthesize the body of a getter which just directly accesses the |
| /// underlying storage. |
| static std::pair<BraceStmt *, bool> |
| synthesizeTrivialGetterBody(AccessorDecl *getter, ASTContext &ctx) { |
| assert(getter->getStorage()->hasStorage()); |
| return synthesizeTrivialGetterBody(getter, TargetImpl::Storage, ctx); |
| } |
| |
| /// Synthesize the body of a getter which just delegates to its superclass |
| /// implementation. |
| static std::pair<BraceStmt *, bool> |
| synthesizeInheritedGetterBody(AccessorDecl *getter, ASTContext &ctx) { |
| // This should call the superclass getter. |
| return synthesizeTrivialGetterBody(getter, TargetImpl::Super, ctx); |
| } |
| |
| /// Synthesize the body of a getter which just delegates to an addressor. |
| static std::pair<BraceStmt *, bool> |
| synthesizeAddressedGetterBody(AccessorDecl *getter, ASTContext &ctx) { |
| assert(getter->getStorage()->getParsedAccessor(AccessorKind::Address)); |
| |
| // This should call the addressor. |
| return synthesizeTrivialGetterBody(getter, TargetImpl::Implementation, ctx); |
| } |
| |
| /// Synthesize the body of a getter which just delegates to a read |
| /// coroutine accessor. |
| static std::pair<BraceStmt *, bool> |
| synthesizeReadCoroutineGetterBody(AccessorDecl *getter, ASTContext &ctx) { |
| assert(getter->getStorage()->getParsedAccessor(AccessorKind::Read)); |
| |
| // This should call the read coroutine. |
| return synthesizeTrivialGetterBody(getter, TargetImpl::Implementation, ctx); |
| } |
| |
| namespace { |
| /// This ASTWalker explores an expression tree looking for expressions (which |
| /// are DeclContext's) and changes their parent DeclContext to NewDC. |
| class RecontextualizeClosures : public ASTWalker { |
| DeclContext *NewDC; |
| public: |
| RecontextualizeClosures(DeclContext *NewDC) : NewDC(NewDC) {} |
| |
| std::pair<bool, Expr *> walkToExprPre(Expr *E) override { |
| // If we find a closure, update its declcontext and do *not* walk into it. |
| if (auto CE = dyn_cast<AbstractClosureExpr>(E)) { |
| CE->setParent(NewDC); |
| return { false, E }; |
| } |
| |
| if (auto CLE = dyn_cast<CaptureListExpr>(E)) { |
| // Make sure to recontextualize any decls in the capture list as well. |
| for (auto &CLE : CLE->getCaptureList()) { |
| CLE.Var->setDeclContext(NewDC); |
| CLE.Init->setDeclContext(NewDC); |
| } |
| } |
| |
| // Unlike a closure, a TapExpr is not a DeclContext, so we need to |
| // recontextualize its variable and then anything else in its body. |
| // FIXME: Might be better to change walkToDeclPre() and walkToStmtPre() |
| // below, but I don't know what other effects that might have. |
| if (auto TE = dyn_cast<TapExpr>(E)) { |
| TE->getVar()->setDeclContext(NewDC); |
| for (auto node : TE->getBody()->getElements()) |
| node.walk(RecontextualizeClosures(NewDC)); |
| } |
| |
| return { true, E }; |
| } |
| |
| /// We don't want to recurse into declarations or statements. |
| bool walkToDeclPre(Decl *) override { return false; } |
| std::pair<bool, Stmt*> walkToStmtPre(Stmt *S) override { return {false,S}; } |
| }; |
| } // end anonymous namespace |
| |
| /// Synthesize the getter for a lazy property with the specified storage |
| /// vardecl. |
| static std::pair<BraceStmt *, bool> |
| synthesizeLazyGetterBody(AccessorDecl *Get, VarDecl *VD, VarDecl *Storage, |
| ASTContext &Ctx) { |
| // The getter checks the optional, storing the initial value in if nil. The |
| // specific pattern we generate is: |
| // get { |
| // if let tmp1 = storage { |
| // return tmp1 |
| // } |
| // let tmp2 : Ty = <<initializer expression>> |
| // storage = tmp2 |
| // return tmp2 |
| // } |
| SmallVector<ASTNode, 6> Body; |
| |
| // Load the existing storage and store it into the 'tmp1' temporary. |
| auto *Tmp1VD = new (Ctx) VarDecl(/*IsStatic*/false, VarDecl::Introducer::Let, |
| SourceLoc(), Ctx.getIdentifier("tmp1"), Get); |
| Tmp1VD->setInterfaceType(VD->getValueInterfaceType()); |
| Tmp1VD->setImplicit(); |
| |
| auto *Named = NamedPattern::createImplicit(Ctx, Tmp1VD); |
| Named->setType(Tmp1VD->getType()); |
| auto *Let = BindingPattern::createImplicit(Ctx, /*let*/ true, Named); |
| Let->setType(Named->getType()); |
| auto *Some = new (Ctx) OptionalSomePattern(Let, SourceLoc()); |
| Some->setImplicit(); |
| Some->setElementDecl(Ctx.getOptionalSomeDecl()); |
| Some->setType(OptionalType::get(Let->getType())); |
| |
| auto *StoredValueExpr = |
| createPropertyLoadOrCallSuperclassGetter(Get, Storage, |
| TargetImpl::Storage, Ctx); |
| SmallVector<StmtConditionElement, 1> Cond; |
| Cond.emplace_back(SourceLoc(), Some, StoredValueExpr); |
| |
| // Build the early return inside the if. |
| auto *Tmp1DRE = new (Ctx) DeclRefExpr(Tmp1VD, DeclNameLoc(), /*Implicit*/true, |
| AccessSemantics::Ordinary); |
| Tmp1DRE->setType(Tmp1VD->getType()); |
| auto *Return = new (Ctx) ReturnStmt(SourceLoc(), Tmp1DRE, |
| /*implicit*/true); |
| |
| |
| // Build the "if" around the early return. |
| Body.push_back(new (Ctx) IfStmt(LabeledStmtInfo(), |
| SourceLoc(), Ctx.AllocateCopy(Cond), Return, |
| /*elseloc*/SourceLoc(), /*else*/nullptr, |
| /*implicit*/ true)); |
| |
| |
| auto *Tmp2VD = new (Ctx) VarDecl(/*IsStatic*/false, VarDecl::Introducer::Let, |
| SourceLoc(), Ctx.getIdentifier("tmp2"), |
| Get); |
| Tmp2VD->setInterfaceType(VD->getValueInterfaceType()); |
| Tmp2VD->setImplicit(); |
| |
| |
| // Take the initializer from the PatternBindingDecl for VD. |
| // TODO: This doesn't work with complicated patterns like: |
| // lazy var (a,b) = foo() |
| auto PBD = VD->getParentPatternBinding(); |
| unsigned entryIndex = PBD->getPatternEntryIndexForVarDecl(VD); |
| |
| Expr *InitValue; |
| if (PBD->getInit(entryIndex)) { |
| PBD->setInitializerSubsumed(entryIndex); |
| |
| if (!PBD->isInitializerChecked(entryIndex)) |
| TypeChecker::typeCheckPatternBinding(PBD, entryIndex); |
| |
| InitValue = PBD->getInit(entryIndex); |
| } else { |
| InitValue = new (Ctx) ErrorExpr(SourceRange(), Tmp2VD->getType()); |
| } |
| |
| // Recontextualize any closure declcontexts nested in the initializer to |
| // realize that they are in the getter function. |
| Get->getImplicitSelfDecl()->setDeclContext(Get); |
| |
| InitValue->walk(RecontextualizeClosures(Get)); |
| |
| // Wrap the initializer in a LazyInitializerExpr to avoid walking it twice. |
| auto initType = InitValue->getType(); |
| InitValue = new (Ctx) LazyInitializerExpr(InitValue); |
| InitValue->setType(initType); |
| |
| Pattern *Tmp2PBDPattern = NamedPattern::createImplicit(Ctx, Tmp2VD); |
| Tmp2PBDPattern = |
| TypedPattern::createImplicit(Ctx, Tmp2PBDPattern, Tmp2VD->getType()); |
| |
| auto *Tmp2PBD = PatternBindingDecl::createImplicit( |
| Ctx, StaticSpellingKind::None, Tmp2PBDPattern, InitValue, Get, |
| /*VarLoc*/ InitValue->getStartLoc()); |
| Body.push_back(Tmp2PBD); |
| Body.push_back(Tmp2VD); |
| |
| // Assign tmp2 into storage. |
| auto Tmp2DRE = new (Ctx) DeclRefExpr(Tmp2VD, DeclNameLoc(), /*Implicit*/true, |
| AccessSemantics::DirectToStorage); |
| Tmp2DRE->setType(Tmp2VD->getType()); |
| createPropertyStoreOrCallSuperclassSetter(Get, Tmp2DRE, Storage, |
| TargetImpl::Storage, Body, Ctx); |
| |
| // Return tmp2. |
| Tmp2DRE = new (Ctx) DeclRefExpr(Tmp2VD, DeclNameLoc(), /*Implicit*/true, |
| AccessSemantics::DirectToStorage); |
| Tmp2DRE->setType(Tmp2VD->getType()); |
| |
| Body.push_back(new (Ctx) ReturnStmt(SourceLoc(), Tmp2DRE, /*implicit*/true)); |
| |
| auto Range = InitValue->getSourceRange(); |
| return { BraceStmt::create(Ctx, Range.Start, Body, Range.End, |
| /*implicit*/true), |
| /*isTypeChecked=*/true }; |
| } |
| |
| /// Synthesize the body of a getter for a property wrapper, which |
| /// delegates to the wrapper's "value" property. |
| static std::pair<BraceStmt *, bool> |
| synthesizePropertyWrapperGetterBody(AccessorDecl *getter, ASTContext &ctx) { |
| return synthesizeTrivialGetterBody(getter, TargetImpl::Wrapper, ctx); |
| } |
| |
| static std::pair<BraceStmt *, bool> |
| synthesizeInvalidAccessor(AccessorDecl *accessor, ASTContext &ctx) { |
| auto loc = accessor->getLoc(); |
| return { BraceStmt::create(ctx, loc, ArrayRef<ASTNode>(), loc, true), true }; |
| } |
| |
| static std::pair<BraceStmt *, bool> |
| synthesizeGetterBody(AccessorDecl *getter, ASTContext &ctx) { |
| auto storage = getter->getStorage(); |
| |
| // Synthesize the getter for a lazy property or property wrapper. |
| if (auto var = dyn_cast<VarDecl>(storage)) { |
| if (var->getAttrs().hasAttribute<LazyAttr>()) { |
| auto *storage = var->getLazyStorageProperty(); |
| return synthesizeLazyGetterBody(getter, var, storage, ctx); |
| } |
| |
| if (var->hasAttachedPropertyWrapper()) { |
| return synthesizePropertyWrapperGetterBody(getter, ctx); |
| } |
| |
| if (var->getOriginalWrappedProperty( |
| PropertyWrapperSynthesizedPropertyKind::Projection)) { |
| return synthesizeTrivialGetterBody(getter, TargetImpl::WrapperStorage, |
| ctx); |
| } |
| } |
| |
| if (getter->hasForcedStaticDispatch()) { |
| return synthesizeTrivialGetterBody(getter, TargetImpl::Ordinary, ctx); |
| } |
| |
| switch (getter->getStorage()->getReadImpl()) { |
| case ReadImplKind::Stored: |
| return synthesizeTrivialGetterBody(getter, ctx); |
| |
| case ReadImplKind::Get: |
| return synthesizeInvalidAccessor(getter, ctx); |
| |
| case ReadImplKind::Inherited: |
| return synthesizeInheritedGetterBody(getter, ctx); |
| |
| case ReadImplKind::Address: |
| return synthesizeAddressedGetterBody(getter, ctx); |
| |
| case ReadImplKind::Read: |
| return synthesizeReadCoroutineGetterBody(getter, ctx); |
| } |
| llvm_unreachable("bad ReadImplKind"); |
| } |
| |
| /// Synthesize the body of a setter which just stores to the given storage |
| /// declaration (which doesn't have to be the storage for the setter). |
| static std::pair<BraceStmt *, bool> |
| synthesizeTrivialSetterBodyWithStorage(AccessorDecl *setter, |
| TargetImpl target, |
| AbstractStorageDecl *storageToUse, |
| ASTContext &ctx) { |
| SourceLoc loc = setter->getStorage()->getLoc(); |
| |
| VarDecl *valueParamDecl = setter->getParameters()->get(0); |
| |
| auto *valueDRE = |
| new (ctx) DeclRefExpr(valueParamDecl, DeclNameLoc(), /*IsImplicit=*/true); |
| valueDRE->setType(valueParamDecl->getType()); |
| |
| SmallVector<ASTNode, 1> setterBody; |
| |
| createPropertyStoreOrCallSuperclassSetter(setter, valueDRE, storageToUse, |
| target, setterBody, ctx); |
| return { BraceStmt::create(ctx, loc, setterBody, loc, true), |
| /*isTypeChecked=*/true }; |
| } |
| |
| static std::pair<BraceStmt *, bool> |
| synthesizeTrivialSetterBody(AccessorDecl *setter, ASTContext &ctx) { |
| auto storage = setter->getStorage(); |
| assert(!isSynthesizedComputedProperty(storage)); |
| |
| return synthesizeTrivialSetterBodyWithStorage(setter, TargetImpl::Storage, |
| storage, ctx); |
| } |
| |
| /// Synthesize the body of a setter for a property wrapper, which |
| /// delegates to the wrapper's "value" property. |
| static std::pair<BraceStmt *, bool> |
| synthesizePropertyWrapperSetterBody(AccessorDecl *setter, ASTContext &ctx) { |
| return synthesizeTrivialSetterBodyWithStorage(setter, TargetImpl::Wrapper, |
| setter->getStorage(), ctx); |
| } |
| |
| /// Synthesize the body of a setter which just delegates to a mutable |
| /// addressor. |
| static std::pair<BraceStmt *, bool> |
| synthesizeMutableAddressSetterBody(AccessorDecl *setter, ASTContext &ctx) { |
| // This should call the mutable addressor. |
| return synthesizeTrivialSetterBodyWithStorage(setter, |
| TargetImpl::Implementation, |
| setter->getStorage(), ctx); |
| } |
| |
| /// Synthesize the body of a setter which just delegates to a modify |
| /// coroutine accessor. |
| static std::pair<BraceStmt *, bool> |
| synthesizeModifyCoroutineSetterBody(AccessorDecl *setter, ASTContext &ctx) { |
| // This should call the modify coroutine. |
| return synthesizeTrivialSetterBodyWithStorage(setter, |
| TargetImpl::Implementation, |
| setter->getStorage(), ctx); |
| } |
| |
| static Expr *maybeWrapInOutExpr(Expr *expr, ASTContext &ctx) { |
| if (auto lvalueType = expr->getType()->getAs<LValueType>()) { |
| auto type = lvalueType->getObjectType(); |
| return new (ctx) InOutExpr(SourceLoc(), expr, type, true); |
| } |
| |
| return expr; |
| } |
| |
| /// Given a VarDecl with a willSet: and/or didSet: specifier, synthesize the |
| /// setter which calls them. |
| static std::pair<BraceStmt *, bool> |
| synthesizeObservedSetterBody(AccessorDecl *Set, TargetImpl target, |
| ASTContext &Ctx, bool isLazy = false) { |
| auto VD = cast<VarDecl>(Set->getStorage()); |
| |
| SourceLoc Loc = VD->getLoc(); |
| |
| // Start by finding the decls for 'self' and 'value'. |
| auto *SelfDecl = Set->getImplicitSelfDecl(); |
| VarDecl *ValueDecl = Set->getParameters()->get(0); |
| |
| bool IsSelfLValue = VD->isSetterMutating(); |
| |
| SubstitutionMap subs; |
| if (auto *genericEnv = Set->getGenericEnvironment()) |
| subs = genericEnv->getForwardingSubstitutionMap(); |
| |
| // The setter loads the oldValue, invokes willSet with the incoming value, |
| // does a direct store, then invokes didSet with the oldValue. |
| SmallVector<ASTNode, 6> SetterBody; |
| |
| auto callObserver = [&](AccessorDecl *observer, VarDecl *arg) { |
| ConcreteDeclRef ref(observer, subs); |
| auto type = observer->getInterfaceType().subst(subs); |
| Expr *Callee = new (Ctx) DeclRefExpr(ref, DeclNameLoc(), /*imp*/true); |
| Callee->setType(type); |
| |
| DeclRefExpr *ValueDRE = nullptr; |
| if (arg) { |
| ValueDRE = new (Ctx) DeclRefExpr(arg, DeclNameLoc(), /*imp*/ true); |
| ValueDRE->setType(arg->getType()); |
| } |
| |
| if (SelfDecl) { |
| auto *SelfDRE = |
| buildSelfReference(SelfDecl, SelfAccessorKind::Peer, IsSelfLValue); |
| SelfDRE = maybeWrapInOutExpr(SelfDRE, Ctx); |
| auto *DSCE = new (Ctx) DotSyntaxCallExpr(Callee, SourceLoc(), SelfDRE); |
| |
| if (auto funcType = type->getAs<FunctionType>()) |
| type = funcType->getResult(); |
| DSCE->setType(type); |
| DSCE->setThrows(false); |
| Callee = DSCE; |
| } |
| |
| CallExpr *Call = nullptr; |
| if (arg) { |
| Call = CallExpr::createImplicit(Ctx, Callee, {ValueDRE}, {Identifier()}); |
| } else { |
| Call = CallExpr::createImplicit(Ctx, Callee, {}, {}); |
| } |
| |
| if (auto funcType = type->getAs<FunctionType>()) |
| type = funcType->getResult(); |
| Call->setType(type); |
| Call->setThrows(false); |
| |
| SetterBody.push_back(Call); |
| }; |
| |
| // If there is a didSet, it will take the old value. Load it into a temporary |
| // 'let' so we have it for later. |
| VarDecl *OldValue = nullptr; |
| if (auto didSet = VD->getParsedAccessor(AccessorKind::DidSet)) { |
| // Only do the load if the didSet body references the implicit oldValue |
| // parameter or it's provided explicitly in the parameter list. |
| if (!didSet->isSimpleDidSet()) { |
| Expr *OldValueExpr = |
| buildStorageReference(Set, VD, isLazy ? TargetImpl::Ordinary : target, |
| /*isUsedForGetAccess=*/true, |
| /*isUsedForSetAccess=*/true, Ctx); |
| |
| // Error recovery. |
| if (OldValueExpr == nullptr) { |
| OldValueExpr = new (Ctx) ErrorExpr(SourceRange(), VD->getType()); |
| } else { |
| OldValueExpr = new (Ctx) LoadExpr(OldValueExpr, VD->getType()); |
| } |
| |
| OldValue = new (Ctx) VarDecl(/*IsStatic*/ false, VarDecl::Introducer::Let, |
| SourceLoc(), Ctx.getIdentifier("tmp"), Set); |
| OldValue->setImplicit(); |
| OldValue->setInterfaceType(VD->getValueInterfaceType()); |
| auto *tmpPattern = NamedPattern::createImplicit(Ctx, OldValue); |
| auto *tmpPBD = PatternBindingDecl::createImplicit( |
| Ctx, StaticSpellingKind::None, tmpPattern, OldValueExpr, Set); |
| SetterBody.push_back(tmpPBD); |
| SetterBody.push_back(OldValue); |
| } |
| } |
| |
| if (auto willSet = VD->getParsedAccessor(AccessorKind::WillSet)) |
| callObserver(willSet, ValueDecl); |
| |
| // Create an assignment into the storage or call to superclass setter. |
| auto *ValueDRE = new (Ctx) DeclRefExpr(ValueDecl, DeclNameLoc(), true); |
| ValueDRE->setType(ValueDecl->getType()); |
| createPropertyStoreOrCallSuperclassSetter( |
| Set, ValueDRE, isLazy ? VD->getLazyStorageProperty() : VD, target, |
| SetterBody, Ctx); |
| |
| if (auto didSet = VD->getParsedAccessor(AccessorKind::DidSet)) |
| callObserver(didSet, OldValue); |
| |
| return { BraceStmt::create(Ctx, Loc, SetterBody, Loc, true), |
| /*isTypeChecked=*/true }; |
| } |
| |
| static std::pair<BraceStmt *, bool> |
| synthesizeStoredWithObserversSetterBody(AccessorDecl *setter, ASTContext &ctx) { |
| return synthesizeObservedSetterBody(setter, TargetImpl::Storage, ctx); |
| } |
| |
| static std::pair<BraceStmt *, bool> |
| synthesizeInheritedWithObserversSetterBody(AccessorDecl *setter, |
| ASTContext &ctx) { |
| return synthesizeObservedSetterBody(setter, TargetImpl::Super, ctx); |
| } |
| |
| static std::pair<BraceStmt *, bool> |
| synthesizeSetterBody(AccessorDecl *setter, ASTContext &ctx) { |
| auto storage = setter->getStorage(); |
| |
| // Synthesize the setter for a lazy property or property wrapper. |
| if (auto var = dyn_cast<VarDecl>(storage)) { |
| if (var->getAttrs().hasAttribute<LazyAttr>()) { |
| // Lazy property setters write to the underlying storage. |
| if (var->hasObservers()) { |
| return synthesizeObservedSetterBody(setter, TargetImpl::Storage, ctx, |
| /*isLazy=*/true); |
| } |
| auto *storage = var->getLazyStorageProperty(); |
| return synthesizeTrivialSetterBodyWithStorage(setter, TargetImpl::Storage, |
| storage, ctx); |
| } |
| |
| if (var->hasAttachedPropertyWrapper()) { |
| if (var->hasObservers()) { |
| return synthesizeObservedSetterBody(setter, TargetImpl::Wrapper, ctx); |
| } |
| |
| return synthesizePropertyWrapperSetterBody(setter, ctx); |
| } |
| |
| // Synthesize a setter for the storage wrapper property of a property |
| // with an attached wrapper. |
| if (auto original = var->getOriginalWrappedProperty( |
| PropertyWrapperSynthesizedPropertyKind::Projection)) { |
| auto backingVar = original->getPropertyWrapperBackingProperty(); |
| return synthesizeTrivialSetterBodyWithStorage(setter, |
| TargetImpl::WrapperStorage, |
| backingVar, ctx); |
| } |
| } |
| |
| switch (storage->getWriteImpl()) { |
| case WriteImplKind::Immutable: |
| llvm_unreachable("synthesizing setter from immutable storage"); |
| |
| case WriteImplKind::Stored: |
| return synthesizeTrivialSetterBody(setter, ctx); |
| |
| case WriteImplKind::StoredWithObservers: |
| return synthesizeStoredWithObserversSetterBody(setter, ctx); |
| |
| case WriteImplKind::InheritedWithObservers: |
| return synthesizeInheritedWithObserversSetterBody(setter, ctx); |
| |
| case WriteImplKind::Set: |
| return synthesizeInvalidAccessor(setter, ctx); |
| |
| case WriteImplKind::MutableAddress: |
| return synthesizeMutableAddressSetterBody(setter, ctx); |
| |
| case WriteImplKind::Modify: |
| return synthesizeModifyCoroutineSetterBody(setter, ctx); |
| } |
| llvm_unreachable("bad WriteImplKind"); |
| } |
| |
| static std::pair<BraceStmt *, bool> |
| synthesizeModifyCoroutineBodyWithSimpleDidSet(AccessorDecl *accessor, |
| ASTContext &ctx) { |
| auto storage = accessor->getStorage(); |
| SourceLoc loc = storage->getLoc(); |
| auto isOverride = storage->getOverriddenDecl(); |
| auto target = isOverride ? TargetImpl::Super : TargetImpl::Storage; |
| |
| SmallVector<ASTNode, 1> body; |
| |
| Expr *ref = buildStorageReference(accessor, storage, target, |
| /*isUsedForGetAccess=*/true, |
| /*isUsedForSetAccess=*/true, |
| ctx); |
| ref = maybeWrapInOutExpr(ref, ctx); |
| |
| YieldStmt *yield = YieldStmt::create(ctx, loc, loc, ref, loc, true); |
| body.push_back(yield); |
| |
| auto Set = storage->getAccessor(AccessorKind::Set); |
| auto DidSet = storage->getAccessor(AccessorKind::DidSet); |
| auto *SelfDecl = accessor->getImplicitSelfDecl(); |
| |
| SubstitutionMap subs; |
| if (auto *genericEnv = Set->getGenericEnvironment()) |
| subs = genericEnv->getForwardingSubstitutionMap(); |
| |
| auto callDidSet = [&]() { |
| ConcreteDeclRef ref(DidSet, subs); |
| auto type = DidSet->getInterfaceType().subst(subs); |
| Expr *Callee = new (ctx) DeclRefExpr(ref, DeclNameLoc(), /*imp*/ true); |
| Callee->setType(type); |
| |
| if (SelfDecl) { |
| auto *SelfDRE = buildSelfReference(SelfDecl, SelfAccessorKind::Peer, |
| storage->isSetterMutating()); |
| SelfDRE = maybeWrapInOutExpr(SelfDRE, ctx); |
| auto *DSCE = new (ctx) DotSyntaxCallExpr(Callee, SourceLoc(), SelfDRE); |
| |
| if (auto funcType = type->getAs<FunctionType>()) |
| type = funcType->getResult(); |
| DSCE->setType(type); |
| DSCE->setThrows(false); |
| Callee = DSCE; |
| } |
| |
| auto *Call = CallExpr::createImplicit(ctx, Callee, {}, {}); |
| if (auto funcType = type->getAs<FunctionType>()) |
| type = funcType->getResult(); |
| Call->setType(type); |
| Call->setThrows(false); |
| |
| body.push_back(Call); |
| }; |
| |
| callDidSet(); |
| |
| return {BraceStmt::create(ctx, loc, body, loc, true), |
| /*isTypeChecked=*/true}; |
| } |
| |
| static std::pair<BraceStmt *, bool> |
| synthesizeCoroutineAccessorBody(AccessorDecl *accessor, ASTContext &ctx) { |
| assert(accessor->isCoroutine()); |
| |
| auto storage = accessor->getStorage(); |
| auto storageReadWriteImpl = storage->getReadWriteImpl(); |
| auto target = (accessor->hasForcedStaticDispatch() |
| ? TargetImpl::Ordinary |
| : TargetImpl::Implementation); |
| |
| // If this is a variable with an attached property wrapper, then |
| // the accessors need to yield the wrappedValue or projectedValue. |
| if (accessor->getAccessorKind() == AccessorKind::Read || |
| storageReadWriteImpl == ReadWriteImplKind::Modify) { |
| if (auto var = dyn_cast<VarDecl>(storage)) { |
| if (var->hasAttachedPropertyWrapper()) { |
| target = TargetImpl::Wrapper; |
| } |
| |
| if (var->getOriginalWrappedProperty( |
| PropertyWrapperSynthesizedPropertyKind::Projection)) { |
| target = TargetImpl::WrapperStorage; |
| } |
| } |
| } |
| |
| SourceLoc loc = storage->getLoc(); |
| SmallVector<ASTNode, 1> body; |
| |
| bool isModify = accessor->getAccessorKind() == AccessorKind::Modify; |
| |
| // Special-case for a modify coroutine of a simple stored property with |
| // observers. We can yield a borrowed copy of the underlying storage |
| // in this case. However, if the accessor was synthesized on-demand, |
| // we do the more general thing, because on-demand accessors might be |
| // serialized, which prevents them from being able to directly reference |
| // didSet/willSet accessors, which are private. |
| if (isModify && |
| !accessor->hasForcedStaticDispatch() && |
| (storageReadWriteImpl == ReadWriteImplKind::StoredWithDidSet || |
| storageReadWriteImpl == ReadWriteImplKind::InheritedWithDidSet) && |
| storage->getParsedAccessor(AccessorKind::DidSet)->isSimpleDidSet()) { |
| return synthesizeModifyCoroutineBodyWithSimpleDidSet(accessor, ctx); |
| } |
| |
| // Build a reference to the storage. |
| Expr *ref = buildStorageReference(accessor, storage, target, |
| /*isUsedForGetAccess=*/true, |
| /*isUsedForSetAccess=*/isModify, |
| ctx); |
| if (ref != nullptr) { |
| // Wrap it with an `&` marker if this is a modify. |
| ref = maybeWrapInOutExpr(ref, ctx); |
| |
| // Yield it. |
| YieldStmt *yield = YieldStmt::create(ctx, loc, loc, ref, loc, true); |
| body.push_back(yield); |
| } |
| |
| return { BraceStmt::create(ctx, loc, body, loc, true), |
| /*isTypeChecked=*/true }; |
| } |
| |
| /// Synthesize the body of a read coroutine. |
| static std::pair<BraceStmt *, bool> |
| synthesizeReadCoroutineBody(AccessorDecl *read, ASTContext &ctx) { |
| assert(read->getStorage()->getReadImpl() != ReadImplKind::Read); |
| return synthesizeCoroutineAccessorBody(read, ctx); |
| } |
| |
| /// Synthesize the body of a modify coroutine. |
| static std::pair<BraceStmt *, bool> |
| synthesizeModifyCoroutineBody(AccessorDecl *modify, ASTContext &ctx) { |
| #ifndef NDEBUG |
| auto storage = modify->getStorage(); |
| auto impl = storage->getReadWriteImpl(); |
| auto hasWrapper = isa<VarDecl>(storage) && |
| cast<VarDecl>(storage)->hasAttachedPropertyWrapper(); |
| assert((hasWrapper || impl != ReadWriteImplKind::Modify) && |
| impl != ReadWriteImplKind::Immutable); |
| #endif |
| return synthesizeCoroutineAccessorBody(modify, ctx); |
| } |
| |
| static std::pair<BraceStmt *, bool> |
| synthesizeAccessorBody(AbstractFunctionDecl *fn, void *) { |
| auto *accessor = cast<AccessorDecl>(fn); |
| auto &ctx = accessor->getASTContext(); |
| |
| if (ctx.Stats) |
| ++ctx.Stats->getFrontendCounters().NumAccessorBodiesSynthesized; |
| |
| switch (accessor->getAccessorKind()) { |
| case AccessorKind::Get: |
| return synthesizeGetterBody(accessor, ctx); |
| |
| case AccessorKind::Set: |
| return synthesizeSetterBody(accessor, ctx); |
| |
| case AccessorKind::Read: |
| return synthesizeReadCoroutineBody(accessor, ctx); |
| |
| case AccessorKind::Modify: |
| return synthesizeModifyCoroutineBody(accessor, ctx); |
| |
| case AccessorKind::WillSet: |
| case AccessorKind::DidSet: |
| case AccessorKind::Address: |
| case AccessorKind::MutableAddress: |
| break; |
| } |
| llvm_unreachable("bad synthesized function kind"); |
| } |
| |
| static void finishImplicitAccessor(AccessorDecl *accessor, |
| ASTContext &ctx) { |
| accessor->setImplicit(); |
| |
| if (ctx.Stats) |
| ++ctx.Stats->getFrontendCounters().NumAccessorsSynthesized; |
| |
| if (doesAccessorHaveBody(accessor)) |
| accessor->setBodySynthesizer(&synthesizeAccessorBody); |
| } |
| |
| static AccessorDecl *createGetterPrototype(AbstractStorageDecl *storage, |
| ASTContext &ctx) { |
| SourceLoc loc = storage->getLoc(); |
| |
| ParamDecl *selfDecl = nullptr; |
| if (storage->getDeclContext()->isTypeContext()) { |
| if (storage->getAttrs().hasAttribute<LazyAttr>()) { |
| // For lazy properties, steal the 'self' from the initializer context. |
| auto *varDecl = cast<VarDecl>(storage); |
| auto *bindingDecl = varDecl->getParentPatternBinding(); |
| const auto i = bindingDecl->getPatternEntryIndexForVarDecl(varDecl); |
| auto *bindingInit = cast<PatternBindingInitializer>( |
| bindingDecl->getInitContext(i)); |
| |
| selfDecl = bindingInit->getImplicitSelfDecl(); |
| } |
| } |
| |
| GenericParamList *genericParams = createAccessorGenericParams(storage); |
| |
| // Add an index-forwarding clause. |
| auto *getterParams = buildIndexForwardingParamList(storage, {}, ctx); |
| |
| SourceLoc staticLoc; |
| if (storage->isStatic()) |
| staticLoc = storage->getLoc(); |
| |
| auto getter = AccessorDecl::create( |
| ctx, loc, /*AccessorKeywordLoc*/ loc, |
| AccessorKind::Get, storage, |
| staticLoc, StaticSpellingKind::None, |
| /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), |
| genericParams, |
| getterParams, |
| Type(), |
| storage->getDeclContext()); |
| |
| // If we're stealing the 'self' from a lazy initializer, set it now. |
| // Note that we don't re-parent the 'self' declaration to be part of |
| // the getter until we synthesize the body of the getter later. |
| if (selfDecl) |
| *getter->getImplicitSelfDeclStorage() = selfDecl; |
| |
| if (storage->isGetterMutating()) |
| getter->setSelfAccessKind(SelfAccessKind::Mutating); |
| else |
| getter->setSelfAccessKind(SelfAccessKind::NonMutating); |
| |
| if (!storage->requiresOpaqueAccessor(AccessorKind::Get)) |
| getter->setForcedStaticDispatch(true); |
| |
| finishImplicitAccessor(getter, ctx); |
| |
| return getter; |
| } |
| |
| static AccessorDecl *createSetterPrototype(AbstractStorageDecl *storage, |
| ASTContext &ctx, |
| AccessorDecl *getter = nullptr) { |
| assert(storage->supportsMutation()); |
| |
| SourceLoc loc = storage->getLoc(); |
| |
| bool isMutating = storage->isSetterMutating(); |
| |
| GenericParamList *genericParams = createAccessorGenericParams(storage); |
| |
| // Add a "(value : T, indices...)" argument list. |
| auto *param = new (ctx) ParamDecl(SourceLoc(), SourceLoc(), |
| Identifier(), loc, |
| ctx.getIdentifier("value"), |
| storage->getDeclContext()); |
| param->setSpecifier(ParamSpecifier::Default); |
| param->setImplicit(); |
| |
| auto *params = buildIndexForwardingParamList(storage, param, ctx); |
| |
| auto setter = AccessorDecl::create( |
| ctx, loc, /*AccessorKeywordLoc*/ SourceLoc(), |
| AccessorKind::Set, storage, |
| /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, |
| /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), |
| genericParams, params, |
| Type(), |
| storage->getDeclContext()); |
| |
| if (isMutating) |
| setter->setSelfAccessKind(SelfAccessKind::Mutating); |
| else |
| setter->setSelfAccessKind(SelfAccessKind::NonMutating); |
| |
| // All mutable storage requires a setter. |
| assert(storage->requiresOpaqueAccessor(AccessorKind::Set)); |
| |
| // Copy availability from the accessor we'll synthesize the setter from. |
| SmallVector<Decl *, 2> asAvailableAs; |
| |
| // That could be a property wrapper... |
| if (auto var = dyn_cast<VarDecl>(storage)) { |
| if (var->hasAttachedPropertyWrapper()) { |
| // The property wrapper info may not actually link back to a wrapper |
| // implementation, if there was a semantic error checking the wrapper. |
| auto info = var->getAttachedPropertyWrapperTypeInfo(0); |
| if (info.valueVar) { |
| if (auto setter = info.valueVar->getOpaqueAccessor(AccessorKind::Set)) { |
| asAvailableAs.push_back(setter); |
| } |
| } |
| } else if (auto wrapperSynthesizedKind |
| = var->getPropertyWrapperSynthesizedPropertyKind()) { |
| switch (*wrapperSynthesizedKind) { |
| case PropertyWrapperSynthesizedPropertyKind::Backing: |
| break; |
| |
| case PropertyWrapperSynthesizedPropertyKind::Projection: { |
| if (auto origVar = var->getOriginalWrappedProperty(wrapperSynthesizedKind)) { |
| // The property wrapper info may not actually link back to a wrapper |
| // implementation, if there was a semantic error checking the wrapper. |
| auto info = origVar->getAttachedPropertyWrapperTypeInfo(0); |
| if (info.projectedValueVar) { |
| if (auto setter |
| = info.projectedValueVar->getOpaqueAccessor(AccessorKind::Set)){ |
| asAvailableAs.push_back(setter); |
| } |
| } |
| } |
| break; |
| } |
| } |
| } |
| } |
| |
| |
| // ...or another accessor. |
| switch (storage->getWriteImpl()) { |
| case WriteImplKind::Immutable: |
| llvm_unreachable("synthesizing setter from immutable storage"); |
| case WriteImplKind::Stored: |
| case WriteImplKind::StoredWithObservers: |
| case WriteImplKind::InheritedWithObservers: |
| case WriteImplKind::Set: |
| // Setter's availability shouldn't be externally influenced in these |
| // cases. |
| break; |
| |
| case WriteImplKind::MutableAddress: |
| if (auto addr = storage->getOpaqueAccessor(AccessorKind::MutableAddress)) { |
| asAvailableAs.push_back(addr); |
| } |
| break; |
| case WriteImplKind::Modify: |
| if (auto mod = storage->getOpaqueAccessor(AccessorKind::Modify)) { |
| asAvailableAs.push_back(mod); |
| } |
| break; |
| } |
| |
| if (!asAvailableAs.empty()) { |
| AvailabilityInference::applyInferredAvailableAttrs( |
| setter, asAvailableAs, ctx); |
| } |
| |
| finishImplicitAccessor(setter, ctx); |
| |
| return setter; |
| } |
| |
| static AccessorDecl * |
| createCoroutineAccessorPrototype(AbstractStorageDecl *storage, |
| AccessorKind kind, |
| ASTContext &ctx) { |
| assert(kind == AccessorKind::Read || kind == AccessorKind::Modify); |
| |
| SourceLoc loc = storage->getLoc(); |
| |
| bool isMutating = storage->isGetterMutating(); |
| if (kind == AccessorKind::Modify) |
| isMutating |= storage->isSetterMutating(); |
| |
| auto dc = storage->getDeclContext(); |
| |
| // The forwarding index parameters. |
| auto *params = buildIndexForwardingParamList(storage, {}, ctx); |
| |
| // Coroutine accessors always return (). |
| const Type retTy = TupleType::getEmpty(ctx); |
| |
| GenericParamList *genericParams = createAccessorGenericParams(storage); |
| |
| auto *accessor = AccessorDecl::create( |
| ctx, loc, /*AccessorKeywordLoc=*/SourceLoc(), |
| kind, storage, |
| /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, |
| /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), |
| genericParams, params, retTy, dc); |
| |
| if (isMutating) |
| accessor->setSelfAccessKind(SelfAccessKind::Mutating); |
| else |
| accessor->setSelfAccessKind(SelfAccessKind::NonMutating); |
| |
| // If the storage does not provide this accessor as an opaque accessor, |
| // we can't add a dynamically-dispatched method entry for the accessor, |
| // so force it to be statically dispatched. ("final" would be inappropriate |
| // because the property can still be overridden.) |
| if (!storage->requiresOpaqueAccessor(kind)) |
| accessor->setForcedStaticDispatch(true); |
| |
| // Make sure the coroutine is available enough to access |
| // the storage (and its getters/setters if it has them). |
| SmallVector<const Decl *, 2> asAvailableAs; |
| asAvailableAs.push_back(storage); |
| if (FuncDecl *getter = storage->getParsedAccessor(AccessorKind::Get)) { |
| asAvailableAs.push_back(getter); |
| } |
| if (kind == AccessorKind::Modify) { |
| if (FuncDecl *setter = storage->getParsedAccessor(AccessorKind::Set)) { |
| asAvailableAs.push_back(setter); |
| } |
| } |
| |
| AvailabilityInference::applyInferredAvailableAttrs(accessor, |
| asAvailableAs, ctx); |
| |
| finishImplicitAccessor(accessor, ctx); |
| |
| return accessor; |
| } |
| |
| static AccessorDecl * |
| createReadCoroutinePrototype(AbstractStorageDecl *storage, |
| ASTContext &ctx) { |
| return createCoroutineAccessorPrototype(storage, AccessorKind::Read, ctx); |
| } |
| |
| static AccessorDecl * |
| createModifyCoroutinePrototype(AbstractStorageDecl *storage, |
| ASTContext &ctx) { |
| return createCoroutineAccessorPrototype(storage, AccessorKind::Modify, ctx); |
| } |
| |
| AccessorDecl * |
| SynthesizeAccessorRequest::evaluate(Evaluator &evaluator, |
| AbstractStorageDecl *storage, |
| AccessorKind kind) const { |
| auto &ctx = storage->getASTContext(); |
| |
| switch (kind) { |
| case AccessorKind::Get: |
| return createGetterPrototype(storage, ctx); |
| |
| case AccessorKind::Set: |
| return createSetterPrototype(storage, ctx); |
| |
| case AccessorKind::Read: |
| return createReadCoroutinePrototype(storage, ctx); |
| |
| case AccessorKind::Modify: |
| return createModifyCoroutinePrototype(storage, ctx); |
| |
| #define OPAQUE_ACCESSOR(ID, KEYWORD) |
| #define ACCESSOR(ID) \ |
| case AccessorKind::ID: |
| #include "swift/AST/AccessorKinds.def" |
| llvm_unreachable("not an opaque accessor"); |
| } |
| llvm_unreachable("Unhandled AccessorKind in switch"); |
| } |
| |
| bool |
| RequiresOpaqueAccessorsRequest::evaluate(Evaluator &evaluator, |
| VarDecl *var) const { |
| // Nameless vars from interface files should not have any accessors. |
| // TODO: Replace this check with a broader check that all storage decls |
| // from interface files have all their accessors up front. |
| if (var->getBaseName().empty()) |
| return false; |
| |
| // Computed properties always require opaque accessors. |
| if (!var->getImplInfo().isSimpleStored()) |
| return true; |
| |
| // The backing storage for a lazy property does require opaque accessors. |
| if (var->isLazyStorageProperty()) |
| return false; |
| |
| auto *dc = var->getDeclContext(); |
| |
| // Local stored variables don't require opaque accessors. |
| if (dc->isLocalContext()) { |
| return false; |
| |
| } else if (dc->isModuleScopeContext()) { |
| // Fixed-layout global variables don't require opaque accessors. |
| if (!var->isResilient() && !var->shouldUseNativeDynamicDispatch()) |
| return false; |
| |
| // Stored properties imported from Clang don't require opaque accessors. |
| } else if (auto *structDecl = dyn_cast<StructDecl>(dc)) { |
| if (structDecl->hasClangNode()) |
| return false; |
| } |
| |
| // Stored properties in SIL mode don't get accessors. |
| // But we might need to create opaque accessors for them. |
| if (auto sourceFile = dc->getParentSourceFile()) { |
| if (sourceFile->Kind == SourceFileKind::SIL) { |
| if (!var->getParsedAccessor(AccessorKind::Get)) |
| return false; |
| } |
| } |
| |
| // Everything else requires opaque accessors. |
| return true; |
| } |
| |
| bool |
| RequiresOpaqueModifyCoroutineRequest::evaluate(Evaluator &evaluator, |
| AbstractStorageDecl *storage) const { |
| // Only for mutable storage. |
| if (!storage->supportsMutation()) |
| return false; |
| |
| auto *dc = storage->getDeclContext(); |
| |
| // Local properties don't have an opaque modify coroutine. |
| if (dc->isLocalContext()) |
| return false; |
| |
| // Fixed-layout global properties don't have an opaque modify coroutine. |
| if (dc->isModuleScopeContext() && !storage->isResilient()) |
| return false; |
| |
| // Imported storage declarations don't have an opaque modify coroutine. |
| if (storage->hasClangNode()) |
| return false; |
| |
| // Dynamic storage does not have an opaque modify coroutine. |
| if (dc->getSelfClassDecl()) |
| if (storage->shouldUseObjCDispatch()) |
| return false; |
| |
| // Requirements of ObjC protocols don't have an opaque modify coroutine. |
| if (auto protoDecl = dyn_cast<ProtocolDecl>(dc)) |
| if (protoDecl->isObjC()) |
| return false; |
| |
| return true; |
| } |
| |
| /// Mark the accessor as transparent if we can. |
| /// |
| /// If the storage is inside a fixed-layout nominal type, we can mark the |
| /// accessor as transparent, since in this case we just want it for abstraction |
| /// purposes (i.e., to make access to the variable uniform and to be able to |
| /// put the getter in a vtable). |
| /// |
| /// If the storage is for a global stored property or a stored property of a |
| /// resilient type, we are synthesizing accessors to present a resilient |
| /// interface to the storage and they should not be transparent. |
| bool |
| IsAccessorTransparentRequest::evaluate(Evaluator &evaluator, |
| AccessorDecl *accessor) const { |
| auto *storage = accessor->getStorage(); |
| if (storage->isTransparent()) |
| return true; |
| |
| if (accessor->getAttrs().hasAttribute<TransparentAttr>()) |
| return true; |
| |
| if (!accessor->isImplicit()) |
| return false; |
| |
| if (!doesAccessorHaveBody(accessor)) |
| return false; |
| |
| auto *DC = accessor->getDeclContext(); |
| auto *nominalDecl = DC->getSelfNominalTypeDecl(); |
| |
| // Global variable accessors are not @_transparent. |
| if (!nominalDecl) |
| return false; |
| |
| // Accessors for resilient properties are not @_transparent. |
| if (storage->isResilient()) |
| return false; |
| |
| // Accessors for classes with @objc ancestry are not @_transparent, |
| // since they use a field offset variable which is not exported. |
| if (auto *classDecl = dyn_cast<ClassDecl>(nominalDecl)) |
| if (classDecl->checkAncestry(AncestryFlags::ObjC)) |
| return false; |
| |
| // Accessors synthesized on-demand are never transaprent. |
| if (accessor->hasForcedStaticDispatch()) |
| return false; |
| |
| if (accessor->getAccessorKind() == AccessorKind::Get || |
| accessor->getAccessorKind() == AccessorKind::Set) { |
| // Getters and setters for lazy properties are not @_transparent. |
| if (storage->getAttrs().hasAttribute<LazyAttr>()) |
| return false; |
| } |
| |
| // Accessors for a property with a wrapper are not @_transparent if |
| // the backing variable has more-restrictive access than the original |
| // property. The same goes for its storage wrapper. |
| if (auto var = dyn_cast<VarDecl>(storage)) { |
| if (auto backingVar = var->getPropertyWrapperBackingProperty()) { |
| if (backingVar->getFormalAccess() < var->getFormalAccess()) |
| return false; |
| } |
| |
| if (auto original = var->getOriginalWrappedProperty( |
| PropertyWrapperSynthesizedPropertyKind::Projection)) { |
| auto backingVar = original->getPropertyWrapperBackingProperty(); |
| if (backingVar->getFormalAccess() < var->getFormalAccess()) |
| return false; |
| } |
| } |
| |
| switch (accessor->getAccessorKind()) { |
| case AccessorKind::Get: |
| break; |
| |
| case AccessorKind::Set: |
| |
| switch (storage->getWriteImpl()) { |
| case WriteImplKind::Set: |
| // Setters for property wrapper are OK, unless there are observers. |
| // FIXME: This should be folded into the WriteImplKind below. |
| if (auto var = dyn_cast<VarDecl>(storage)) { |
| if (var->hasAttachedPropertyWrapper()) { |
| if (var->hasObservers()) |
| return false; |
| |
| break; |
| } else if (var->getOriginalWrappedProperty( |
| PropertyWrapperSynthesizedPropertyKind::Projection)) { |
| break; |
| } |
| } |
| |
| // Anything else should not have a synthesized setter. |
| LLVM_FALLTHROUGH; |
| case WriteImplKind::Immutable: |
| if (accessor->getASTContext().LangOpts.AllowModuleWithCompilerErrors) |
| return false; |
| llvm_unreachable("should not be synthesizing accessor in this case"); |
| |
| case WriteImplKind::StoredWithObservers: |
| case WriteImplKind::InheritedWithObservers: |
| // Setters for observed properties are not @_transparent (because the |
| // observers are private) and cannot be referenced from a transparent |
| // method). |
| return false; |
| |
| case WriteImplKind::Stored: |
| case WriteImplKind::MutableAddress: |
| case WriteImplKind::Modify: |
| break; |
| } |
| break; |
| |
| case AccessorKind::Read: |
| case AccessorKind::Modify: |
| break; |
| |
| case AccessorKind::WillSet: |
| case AccessorKind::DidSet: |
| case AccessorKind::Address: |
| case AccessorKind::MutableAddress: |
| llvm_unreachable("bad synthesized function kind"); |
| } |
| |
| switch (storage->getReadWriteImpl()) { |
| case ReadWriteImplKind::StoredWithDidSet: |
| case ReadWriteImplKind::InheritedWithDidSet: |
| if (storage->getAccessor(AccessorKind::DidSet)->isSimpleDidSet()) |
| return false; |
| break; |
| default: |
| break; |
| } |
| |
| return true; |
| } |
| |
| VarDecl * |
| LazyStoragePropertyRequest::evaluate(Evaluator &evaluator, |
| VarDecl *VD) const { |
| assert(isa<SourceFile>(VD->getDeclContext()->getModuleScopeContext())); |
| assert(VD->getAttrs().hasAttribute<LazyAttr>()); |
| auto &Context = VD->getASTContext(); |
| |
| // Create the storage property as an optional of VD's type. |
| SmallString<64> NameBuf; |
| NameBuf += "$__lazy_storage_$_"; |
| NameBuf += VD->getName().str(); |
| auto StorageName = Context.getIdentifier(NameBuf); |
| auto StorageInterfaceTy = OptionalType::get(VD->getInterfaceType()); |
| auto StorageTy = OptionalType::get(VD->getType()); |
| |
| auto *Storage = new (Context) VarDecl(/*IsStatic*/false, VarDecl::Introducer::Var, |
| VD->getLoc(), StorageName, |
| VD->getDeclContext()); |
| Storage->setInterfaceType(StorageInterfaceTy); |
| Storage->setLazyStorageProperty(true); |
| Storage->setUserAccessible(false); |
| |
| // The storage is implicit and private. |
| Storage->setImplicit(); |
| Storage->overwriteAccess(AccessLevel::Private); |
| Storage->overwriteSetterAccess(AccessLevel::Private); |
| |
| addMemberToContextIfNeeded(Storage, VD->getDeclContext(), VD); |
| |
| // Create the pattern binding decl for the storage decl. This will get |
| // default initialized to nil. |
| Pattern *PBDPattern = NamedPattern::createImplicit(Context, Storage); |
| PBDPattern->setType(StorageTy); |
| PBDPattern = TypedPattern::createImplicit(Context, PBDPattern, StorageTy); |
| auto *InitExpr = new (Context) NilLiteralExpr(SourceLoc(), /*Implicit=*/true); |
| InitExpr->setType(Storage->getType()); |
| |
| auto *PBD = PatternBindingDecl::createImplicit( |
| Context, StaticSpellingKind::None, PBDPattern, InitExpr, |
| VD->getDeclContext(), /*VarLoc*/ VD->getLoc()); |
| PBD->setInitializerChecked(0); |
| |
| addMemberToContextIfNeeded(PBD, VD->getDeclContext(), Storage); |
| |
| return Storage; |
| } |
| |
| /// Synthesize a computed property `$foo` for a property with an attached |
| /// wrapper that has a `projectedValue` property. |
| static VarDecl *synthesizePropertyWrapperProjectionVar( |
| ASTContext &ctx, VarDecl *var, Type wrapperType, |
| VarDecl *wrapperVar) { |
| // If the original property has a @_projectedValueProperty attribute, use |
| // that to find the storage wrapper property. |
| if (auto attr = var->getAttrs().getAttribute<ProjectedValuePropertyAttr>()){ |
| SmallVector<ValueDecl *, 2> declsFound; |
| DeclNameRef projectionName(attr->ProjectionPropertyName); |
| auto dc = var->getDeclContext(); |
| if (dc->isTypeContext()) { |
| dc->lookupQualified(dc->getSelfNominalTypeDecl(), projectionName, |
| NL_QualifiedDefault, declsFound); |
| } else if (dc->isModuleScopeContext()) { |
| dc->lookupQualified(dc->getParentModule(), projectionName, |
| NL_QualifiedDefault, declsFound); |
| } else { |
| llvm_unreachable("Property wrappers don't work in local contexts"); |
| } |
| |
| if (declsFound.size() == 1 && isa<VarDecl>(declsFound.front())) { |
| auto property = cast<VarDecl>(declsFound.front()); |
| property->setOriginalWrappedProperty(var); |
| return property; |
| } |
| |
| ctx.Diags.diagnose(attr->getLocation(), |
| diag::property_wrapper_projection_value_missing, |
| projectionName); |
| attr->setInvalid(); |
| } |
| |
| // Compute the name of the storage type. |
| SmallString<64> nameBuf; |
| nameBuf = "$"; |
| nameBuf += var->getName().str(); |
| Identifier name = ctx.getIdentifier(nameBuf); |
| |
| // Determine the type of the property. |
| Type propertyType = wrapperType->getTypeOfMember( |
| var->getModuleContext(), wrapperVar, |
| wrapperVar->getValueInterfaceType()); |
| |
| // Form the property. |
| auto dc = var->getDeclContext(); |
| VarDecl *property = new (ctx) VarDecl(/*IsStatic=*/var->isStatic(), |
| VarDecl::Introducer::Var, |
| var->getLoc(), |
| name, dc); |
| property->setInterfaceType(propertyType); |
| property->setImplicit(); |
| property->setOriginalWrappedProperty(var); |
| addMemberToContextIfNeeded(property, dc, var); |
| |
| // Create the pattern binding declaration for the property. |
| Pattern *pbdPattern = NamedPattern::createImplicit(ctx, property); |
| pbdPattern->setType(propertyType); |
| pbdPattern = TypedPattern::createImplicit(ctx, pbdPattern, propertyType); |
| auto pbd = PatternBindingDecl::createImplicit( |
| ctx, property->getCorrectStaticSpelling(), pbdPattern, |
| /*init*/nullptr, dc, SourceLoc()); |
| addMemberToContextIfNeeded(pbd, dc, var); |
| pbd->setStatic(var->isStatic()); |
| |
| // Determine the access level for the property. |
| property->overwriteAccess(var->getFormalAccess()); |
| |
| // Determine setter access. |
| 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 |
| property->setImplInfo(StorageImplInfo::getImmutableComputed()); |
| |
| var->getAttrs().add( |
| new (ctx) ProjectedValuePropertyAttr(name, SourceLoc(), SourceRange(), |
| /*Implicit=*/true)); |
| return property; |
| } |
| |
| static void typeCheckSynthesizedWrapperInitializer( |
| PatternBindingDecl *pbd, VarDecl *backingVar, PatternBindingDecl *parentPBD, |
| Expr *&initializer) { |
| // Figure out the context in which the initializer was written. |
| DeclContext *originalDC = parentPBD->getDeclContext(); |
| if (!originalDC->isLocalContext()) { |
| auto initContext = |
| cast_or_null<PatternBindingInitializer>(parentPBD->getInitContext(0)); |
| if (initContext) |
| originalDC = initContext; |
| } |
| |
| // Type-check the initialization. |
| { |
| auto *wrappedVar = backingVar->getOriginalWrappedProperty(); |
| auto i = parentPBD->getPatternEntryIndexForVarDecl(wrappedVar); |
| auto *pattern = parentPBD->getPattern(i); |
| TypeChecker::typeCheckBinding(pattern, initializer, originalDC, |
| wrappedVar->getType(), parentPBD, i); |
| } |
| |
| const auto i = pbd->getPatternEntryIndexForVarDecl(backingVar); |
| if (auto initializerContext = |
| dyn_cast_or_null<Initializer>(pbd->getInitContext(i))) { |
| TypeChecker::contextualizeInitializer(initializerContext, initializer); |
| } |
| checkPropertyWrapperActorIsolation(pbd, initializer); |
| TypeChecker::checkPropertyWrapperEffects(pbd, initializer); |
| } |
| |
| static PropertyWrapperMutability::Value |
| getGetterMutatingness(VarDecl *var) { |
| return var->isGetterMutating() |
| ? PropertyWrapperMutability::Mutating |
| : PropertyWrapperMutability::Nonmutating; |
| } |
| |
| static PropertyWrapperMutability::Value |
| getSetterMutatingness(VarDecl *var, DeclContext *dc) { |
| if (!var->isSettable(nullptr) || |
| !var->isSetterAccessibleFrom(dc)) |
| return PropertyWrapperMutability::DoesntExist; |
| |
| return var->isSetterMutating() |
| ? PropertyWrapperMutability::Mutating |
| : PropertyWrapperMutability::Nonmutating; |
| } |
| |
| Optional<PropertyWrapperMutability> |
| PropertyWrapperMutabilityRequest::evaluate(Evaluator &, |
| VarDecl *var) const { |
| VarDecl *originalVar = var; |
| unsigned numWrappers = originalVar->getAttachedPropertyWrappers().size(); |
| bool isProjectedValue = false; |
| if (numWrappers < 1) { |
| originalVar = var->getOriginalWrappedProperty( |
| PropertyWrapperSynthesizedPropertyKind::Projection); |
| if (!originalVar) |
| return None; |
| |
| numWrappers = originalVar->getAttachedPropertyWrappers().size(); |
| isProjectedValue = true; |
| } |
| |
| // Make sure we don't ignore .swiftinterface files, because those will |
| // have the accessors printed |
| auto varSourceFile = var->getDeclContext()->getParentSourceFile(); |
| auto isVarNotInInterfaceFile = |
| varSourceFile && varSourceFile->Kind != SourceFileKind::Interface; |
| |
| if (var->getParsedAccessor(AccessorKind::Get) && isVarNotInInterfaceFile) |
| return None; |
| if (var->getParsedAccessor(AccessorKind::Set) && isVarNotInInterfaceFile) |
| return None; |
| |
| // Figure out which member we're looking through. |
| auto varMember = isProjectedValue |
| ? &PropertyWrapperTypeInfo::projectedValueVar |
| : &PropertyWrapperTypeInfo::valueVar; |
| |
| // Start with the traits from the outermost wrapper. |
| auto firstWrapper = originalVar->getAttachedPropertyWrapperTypeInfo(0); |
| if (firstWrapper.*varMember == nullptr) |
| return None; |
| |
| PropertyWrapperMutability result; |
| |
| result.Getter = getGetterMutatingness(firstWrapper.*varMember); |
| result.Setter = getSetterMutatingness(firstWrapper.*varMember, |
| var->getInnermostDeclContext()); |
| |
| auto getCustomAttrTypeLoc = [](const CustomAttr *CA) -> TypeLoc { |
| return { CA->getTypeRepr(), CA->getType() }; |
| }; |
| |
| // Compose the traits of the following wrappers. |
| for (unsigned i = 1; i < numWrappers && !isProjectedValue; ++i) { |
| assert(var == originalVar); |
| auto wrapper = var->getAttachedPropertyWrapperTypeInfo(i); |
| if (!wrapper.valueVar) |
| return None; |
| |
| PropertyWrapperMutability nextResult; |
| nextResult.Getter = |
| result.composeWith(getGetterMutatingness(wrapper.valueVar)); |
| // A property must have a getter, so we can't compose a wrapper that |
| // exposes a mutating getter wrapped inside a get-only wrapper. |
| if (nextResult.Getter == PropertyWrapperMutability::DoesntExist) { |
| auto &ctx = var->getASTContext(); |
| ctx.Diags.diagnose(var->getAttachedPropertyWrappers()[i]->getLocation(), |
| diag::property_wrapper_mutating_get_composed_to_get_only, |
| getCustomAttrTypeLoc(var->getAttachedPropertyWrappers()[i]), |
| getCustomAttrTypeLoc(var->getAttachedPropertyWrappers()[i-1])); |
| |
| return None; |
| } |
| nextResult.Setter = |
| result.composeWith(getSetterMutatingness(wrapper.valueVar, |
| var->getInnermostDeclContext())); |
| result = nextResult; |
| } |
| assert(result.Getter != PropertyWrapperMutability::DoesntExist |
| && "getter must exist"); |
| return result; |
| } |
| |
| Optional<PropertyWrapperLValueness> |
| PropertyWrapperLValuenessRequest::evaluate(Evaluator &, |
| VarDecl *var) const { |
| VarDecl *VD = var; |
| unsigned numWrappers = var->getAttachedPropertyWrappers().size(); |
| bool isProjectedValue = false; |
| if (numWrappers < 1) { |
| VD = var->getOriginalWrappedProperty( |
| PropertyWrapperSynthesizedPropertyKind::Projection); |
| numWrappers = 1; // Can't compose projected values |
| isProjectedValue = true; |
| } |
| |
| if (!VD) |
| return None; |
| |
| auto varMember = isProjectedValue |
| ? &PropertyWrapperTypeInfo::projectedValueVar |
| : &PropertyWrapperTypeInfo::valueVar; |
| |
| auto accessorMutability = [&](unsigned wrapperIndex) -> PropertyWrapperMutability { |
| PropertyWrapperMutability mutability; |
| auto wrapperInfo = VD->getAttachedPropertyWrapperTypeInfo(wrapperIndex); |
| mutability.Getter = getGetterMutatingness(wrapperInfo.*varMember); |
| mutability.Setter = getSetterMutatingness(wrapperInfo.*varMember, |
| var->getInnermostDeclContext()); |
| return mutability; |
| }; |
| |
| // Calling the getter (or setter) on the nth property wrapper in the chain |
| // is done as follows: |
| // 1. call the getter on the (n-1)th property wrapper instance to get the |
| // nth property wrapper instance |
| // 2. call the getter (or setter) on the nth property wrapper instance |
| // 3. if (2) is a mutating access, call the setter on the (n-1)th property |
| // wrapper instance to write back the mutated value |
| |
| // Below, we determine which of these property wrapper instances need to be |
| // accessed mutating-ly, and therefore should be l-values. |
| |
| unsigned innermostWrapperIdx = numWrappers - 1; |
| auto lastAccess = accessorMutability(innermostWrapperIdx); |
| |
| PropertyWrapperLValueness lvalueness(numWrappers); |
| lvalueness.isLValueForGetAccess[innermostWrapperIdx] = |
| lastAccess.Getter == PropertyWrapperMutability::Mutating; |
| lvalueness.isLValueForSetAccess[innermostWrapperIdx] = |
| lastAccess.Setter == PropertyWrapperMutability::Mutating; |
| |
| auto lastAccessForGet = lastAccess.Getter; |
| auto lastAccessForSet = lastAccess.Setter; |
| for (int i = innermostWrapperIdx - 1; i >= 0; --i) { |
| auto access = accessorMutability(i); |
| |
| lastAccessForGet = access.composeWith(lastAccessForGet); |
| lastAccessForSet = access.composeWith(lastAccessForSet); |
| |
| lvalueness.isLValueForGetAccess[i] = |
| lastAccessForGet == PropertyWrapperMutability::Mutating; |
| lvalueness.isLValueForSetAccess[i] = |
| lastAccessForSet == PropertyWrapperMutability::Mutating; |
| } |
| |
| return lvalueness; |
| } |
| |
| 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(); |
| |
| // Compute the name of the storage type. |
| ASTContext &ctx = var->getASTContext(); |
| SmallString<64> nameBuf; |
| nameBuf = "_"; |
| nameBuf += var->getName().str(); |
| Identifier name = ctx.getIdentifier(nameBuf); |
| |
| // Determine the type of the storage. |
| auto dc = var->getDeclContext(); |
| 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); |
| } |
| |
| 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); |
| 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 there is a projection property (projectedValue) in the wrapper, |
| // synthesize a computed property for '$foo'. |
| VarDecl *storageVar = nullptr; |
| if (wrapperInfo.projectedValueVar) { |
| storageVar = synthesizePropertyWrapperProjectionVar( |
| ctx, var, storageInterfaceType, wrapperInfo.projectedValueVar); |
| } |
| |
| // If no initial wrapped value was provided via '=' and either: |
| // 1. Not all of the attached property wrappers have init(wrappedValue:), or |
| // 2. An initializer has already been synthesized from arguments in the |
| // property wrapper attribute, |
| // then this property wrapper cannot be initialized out-of-line with a wrapped |
| // value. |
| if (!wrappedValue && (!var->allAttachedPropertyWrappersHaveWrappedValueInit() || |
| initializer)) { |
| return PropertyWrapperBackingPropertyInfo( |
| backingVar, storageVar, nullptr, nullptr); |
| } |
| |
| // Form the initialization of the backing property from a value of the |
| // original property's type. |
| if (!initializer) { |
| initializer = PropertyWrapperValuePlaceholderExpr::create( |
| ctx, var->getSourceRange(), var->getType(), /*wrappedValue=*/nullptr); |
| typeCheckSynthesizedWrapperInitializer( |
| pbd, backingVar, parentPBD, initializer); |
| wrappedValue = findWrappedValuePlaceholder(initializer); |
| } |
| |
| return PropertyWrapperBackingPropertyInfo(backingVar, storageVar, |
| initializer, wrappedValue); |
| } |
| |
| /// 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, |
| StorageImplInfo &info) { |
| if (auto *var = dyn_cast<VarDecl>(storage)) { |
| SourceLoc typeLoc; |
| if (auto *repr = var->getTypeReprOrParentPatternTypeRepr()) |
| typeLoc = repr->getLoc(); |
| |
| if (info.hasStorage()) { |
| // Protocols cannot have stored properties. |
| if (var->isLet()) { |
| var->diagnose(diag::protocol_property_must_be_computed_var) |
| .fixItReplace(var->getParentPatternBinding()->getLoc(), "var") |
| .fixItInsertAfter(typeLoc, " { get }"); |
| } else { |
| auto diag = var->diagnose(diag::protocol_property_must_be_computed); |
| auto braces = var->getBracesRange(); |
| |
| if (braces.isValid()) |
| diag.fixItReplace(braces, "{ get <#set#> }"); |
| else |
| diag.fixItInsertAfter(typeLoc, " { get <#set#> }"); |
| } |
| } |
| } |
| |
| auto protocol = cast<ProtocolDecl>(storage->getDeclContext()); |
| if (protocol->isObjC()) { |
| info = StorageImplInfo::getComputed(info.supportsMutation()); |
| } else { |
| info = StorageImplInfo::getOpaque(info.supportsMutation(), |
| storage->getOpaqueReadOwnership()); |
| } |
| } |
| |
| /// This emits a diagnostic with a fixit to remove the attribute. |
| template<typename ...ArgTypes> |
| void diagnoseAndRemoveAttr(Decl *D, DeclAttribute *attr, |
| ArgTypes &&...Args) { |
| auto &ctx = D->getASTContext(); |
| ctx.Diags.diagnose(attr->getLocation(), std::forward<ArgTypes>(Args)...) |
| .fixItRemove(attr->getRangeWithAt()); |
| } |
| |
| static void finishLazyVariableImplInfo(VarDecl *var, |
| StorageImplInfo &info) { |
| auto *attr = var->getAttrs().getAttribute<LazyAttr>(); |
| |
| // It cannot currently be used on let's since we don't have a mutability model |
| // that supports it. |
| if (var->isLet()) |
| diagnoseAndRemoveAttr(var, attr, diag::lazy_not_on_let); |
| |
| // lazy must have an initializer. |
| if (!var->getParentInitializer()) |
| diagnoseAndRemoveAttr(var, attr, diag::lazy_requires_initializer); |
| |
| bool invalid = false; |
| |
| if (isa<ProtocolDecl>(var->getDeclContext())) { |
| diagnoseAndRemoveAttr(var, attr, diag::lazy_not_in_protocol); |
| invalid = true; |
| } |
| |
| // Lazy properties must be written as stored properties in the source. |
| if (info.getReadImpl() != ReadImplKind::Stored && |
| (info.getWriteImpl() != WriteImplKind::Stored && |
| info.getWriteImpl() != WriteImplKind::StoredWithObservers)) { |
| diagnoseAndRemoveAttr(var, attr, diag::lazy_not_on_computed); |
| invalid = true; |
| } |
| |
| // The pattern binding must only bind a single variable. |
| if (!var->getParentPatternBinding()->getSingleVar()) |
| diagnoseAndRemoveAttr(var, attr, diag::lazy_requires_single_var); |
| |
| if (!invalid) |
| info = StorageImplInfo::getMutableComputed(); |
| } |
| |
| static void finishPropertyWrapperImplInfo(VarDecl *var, |
| StorageImplInfo &info) { |
| auto parentSF = var->getDeclContext()->getParentSourceFile(); |
| if (!parentSF) |
| return; |
| |
| // Properties with wrappers must not declare a getter or setter. |
| if (!info.hasStorage() && parentSF->Kind != SourceFileKind::Interface) { |
| auto &ctx = parentSF->getASTContext(); |
| for (auto attr : var->getAttrs().getAttributes<CustomAttr>()) |
| ctx.Diags.diagnose(attr->getLocation(), diag::property_wrapper_computed); |
| |
| return; |
| } |
| |
| bool wrapperSetterIsUsable = false; |
| if (var->getParsedAccessor(AccessorKind::Set)) { |
| wrapperSetterIsUsable = true; |
| } else if (parentSF && parentSF->Kind != SourceFileKind::Interface |
| && !var->isLet()) { |
| if (auto comp = var->getPropertyWrapperMutability()) { |
| wrapperSetterIsUsable = |
| comp->Setter != PropertyWrapperMutability::DoesntExist; |
| } else { |
| wrapperSetterIsUsable = true; |
| } |
| } |
| |
| if (!wrapperSetterIsUsable) { |
| info = StorageImplInfo::getImmutableComputed(); |
| return; |
| } |
| |
| if (var->hasObservers()) { |
| info = StorageImplInfo::getMutableComputed(); |
| } else { |
| info = StorageImplInfo(ReadImplKind::Get, WriteImplKind::Set, |
| ReadWriteImplKind::Modify); |
| } |
| } |
| |
| static void finishNSManagedImplInfo(VarDecl *var, |
| StorageImplInfo &info) { |
| auto *attr = var->getAttrs().getAttribute<NSManagedAttr>(); |
| |
| if (var->isLet()) |
| diagnoseAndRemoveAttr(var, attr, diag::attr_NSManaged_let_property); |
| |
| SourceFile *parentFile = var->getDeclContext()->getParentSourceFile(); |
| |
| auto diagnoseNotStored = [&](unsigned kind) { |
| // Skip diagnosing @NSManaged declarations in module interfaces. They are |
| // properties that are stored, but have specially synthesized observers |
| // and we should allow them to have getters and setters in a module |
| // interface. |
| if (parentFile && parentFile->Kind == SourceFileKind::Interface) |
| return; |
| |
| diagnoseAndRemoveAttr(var, attr, diag::attr_NSManaged_not_stored, kind); |
| }; |
| |
| // @NSManaged properties must be written as stored. |
| if (info.isSimpleStored()) { |
| // @NSManaged properties end up being computed; complain if there is |
| // an initializer. |
| if (var->getParentInitializer()) { |
| auto &Diags = var->getASTContext().Diags; |
| Diags.diagnose(attr->getLocation(), diag::attr_NSManaged_initial_value) |
| .highlight(var->getParentInitializer()->getSourceRange()); |
| } |
| |
| // Otherwise, ok. |
| info = StorageImplInfo::getMutableComputed(); |
| |
| } else if (info.getReadImpl() == ReadImplKind::Address || |
| info.getWriteImpl() == WriteImplKind::MutableAddress) { |
| diagnoseNotStored(/*addressed*/ 2); |
| } else if (info.getWriteImpl() == WriteImplKind::StoredWithObservers || |
| info.getWriteImpl() == WriteImplKind::InheritedWithObservers) { |
| diagnoseNotStored(/*observing*/ 1); |
| } else { |
| diagnoseNotStored(/*computed*/ 0); |
| } |
| } |
| |
| static void finishStorageImplInfo(AbstractStorageDecl *storage, |
| StorageImplInfo &info) { |
| auto dc = storage->getDeclContext(); |
| |
| if (auto var = dyn_cast<VarDecl>(storage)) { |
| if (!info.hasStorage()) { |
| if (auto *init = var->getParentInitializer()) { |
| auto &Diags = var->getASTContext().Diags; |
| Diags.diagnose(init->getLoc(), diag::getset_init) |
| .highlight(init->getSourceRange()); |
| } |
| } |
| |
| if (var->getAttrs().hasAttribute<LazyAttr>()) { |
| finishLazyVariableImplInfo(var, info); |
| } else if (var->getAttrs().hasAttribute<NSManagedAttr>()) { |
| finishNSManagedImplInfo(var, info); |
| } else if (var->hasAttachedPropertyWrapper()) { |
| finishPropertyWrapperImplInfo(var, info); |
| } |
| } |
| |
| if (isa<ProtocolDecl>(dc)) |
| finishProtocolStorageImplInfo(storage, info); |
| |
| // If we have a stored property in an unsupported context, diagnose |
| // and change it to computed to avoid confusing SILGen. |
| |
| // Note: Stored properties in protocols are diagnosed in |
| // finishProtocolStorageImplInfo(). |
| |
| if (info.hasStorage() && !storage->isStatic()) { |
| if (isa<EnumDecl>(dc)) { |
| storage->diagnose(diag::enum_stored_property); |
| info = StorageImplInfo::getMutableComputed(); |
| } else if (isa<ExtensionDecl>(dc) && |
| !storage->getAttrs().getAttribute<DynamicReplacementAttr>()) { |
| storage->diagnose(diag::extension_stored_property); |
| |
| info = (info.supportsMutation() |
| ? StorageImplInfo::getMutableComputed() |
| : StorageImplInfo::getImmutableComputed()); |
| } |
| } |
| } |
| |
| /// Gets the storage info of the provided storage decl if it has the |
| /// @_hasStorage attribute and it's not in SIL mode. |
| /// |
| /// In this case, we say the decl is: |
| /// |
| /// Read: |
| /// - Stored, always |
| /// Write: |
| /// - Stored, if the decl is a 'var'. |
| /// - StoredWithObservers, if the decl has a setter |
| /// - This indicates that the original decl had a 'didSet' and/or 'willSet' |
| /// - InheritedWithObservers, if the decl has a setter and is an overridde. |
| /// - Immutable, if the decl is a 'let' or it does not have a setter. |
| /// ReadWrite: |
| /// - Stored, if the decl has no accessors listed. |
| /// - Immutable, if the decl is a 'let' or it does not have a setter. |
| /// - MaterializeToTemporary, if the decl has a setter. |
| static StorageImplInfo classifyWithHasStorageAttr(VarDecl *var) { |
| WriteImplKind writeImpl; |
| ReadWriteImplKind readWriteImpl; |
| |
| if (var->getParsedAccessor(AccessorKind::Get) && |
| var->getParsedAccessor(AccessorKind::Set)) { |
| // If we see `@_hasStorage var x: T { get set }`, then our property has |
| // willSet/didSet observers. |
| writeImpl = var->getAttrs().hasAttribute<OverrideAttr>() ? |
| WriteImplKind::InheritedWithObservers : |
| WriteImplKind::StoredWithObservers; |
| readWriteImpl = ReadWriteImplKind::MaterializeToTemporary; |
| } else if (var->isLet()) { |
| writeImpl = WriteImplKind::Immutable; |
| readWriteImpl = ReadWriteImplKind::Immutable; |
| } else { |
| // Default to stored writes. |
| writeImpl = WriteImplKind::Stored; |
| readWriteImpl = ReadWriteImplKind::Stored; |
| } |
| |
| // Always force Stored reads if @_hasStorage is present. |
| return StorageImplInfo(ReadImplKind::Stored, writeImpl, readWriteImpl); |
| } |
| |
| StorageImplInfo |
| StorageImplInfoRequest::evaluate(Evaluator &evaluator, |
| AbstractStorageDecl *storage) const { |
| if (auto *param = dyn_cast<ParamDecl>(storage)) { |
| return StorageImplInfo::getSimpleStored( |
| param->isInOut() |
| ? StorageIsMutable |
| : StorageIsNotMutable); |
| } |
| |
| if (auto *var = dyn_cast<VarDecl>(storage)) { |
| // Allow the @_hasStorage attribute to override all the accessors we parsed |
| // when making the final classification. |
| if (var->getAttrs().hasAttribute<HasStorageAttr>()) { |
| // The SIL rules for @_hasStorage are slightly different from the non-SIL |
| // rules. In SIL mode, @_hasStorage marks that the type is simply stored, |
| // and the only thing that determines mutability is the existence of the |
| // setter. |
| // |
| // FIXME: SIL should not be special cased here. The behavior should be |
| // consistent between SIL and non-SIL. |
| // The strategy here should be to keep track of all opaque accessors |
| // along with enough information to access the storage trivially |
| // if allowed. This could be a representational change to |
| // StorageImplInfo such that it keeps a bitset of listed accessors |
| // and dynamically determines the access strategy from that. |
| auto *SF = storage->getDeclContext()->getParentSourceFile(); |
| if (SF && SF->Kind == SourceFileKind::SIL) |
| return StorageImplInfo::getSimpleStored( |
| var->getParsedAccessor(AccessorKind::Set) |
| ? StorageIsMutable |
| : StorageIsNotMutable); |
| |
| return classifyWithHasStorageAttr(var); |
| } |
| } |
| |
| bool hasWillSet = storage->getParsedAccessor(AccessorKind::WillSet); |
| bool hasDidSet = storage->getParsedAccessor(AccessorKind::DidSet); |
| bool hasSetter = storage->getParsedAccessor(AccessorKind::Set); |
| bool hasModify = storage->getParsedAccessor(AccessorKind::Modify); |
| bool hasMutableAddress = storage->getParsedAccessor(AccessorKind::MutableAddress); |
| |
| // 'get', 'read', and a non-mutable addressor are all exclusive. |
| ReadImplKind readImpl; |
| if (storage->getParsedAccessor(AccessorKind::Get)) { |
| readImpl = ReadImplKind::Get; |
| } else if (storage->getParsedAccessor(AccessorKind::Read)) { |
| readImpl = ReadImplKind::Read; |
| } else if (storage->getParsedAccessor(AccessorKind::Address)) { |
| readImpl = ReadImplKind::Address; |
| |
| // If there's a writing accessor of any sort, there must also be a |
| // reading accessor. |
| } else if (hasSetter || hasModify || hasMutableAddress) { |
| readImpl = ReadImplKind::Get; |
| |
| // Subscripts always have to have some sort of accessor; they can't be |
| // purely stored. |
| } else if (isa<SubscriptDecl>(storage)) { |
| readImpl = ReadImplKind::Get; |
| |
| // Check if we have observers. |
| } else if (hasWillSet || hasDidSet) { |
| if (storage->getAttrs().hasAttribute<OverrideAttr>() && |
| storage->getDeclContext()->isTypeContext()) { |
| readImpl = ReadImplKind::Inherited; |
| } else { |
| readImpl = ReadImplKind::Stored; |
| } |
| |
| // Extensions can't have stored properties. If there are braces, assume |
| // this is an incomplete computed property. This avoids an "extensions |
| // must not contain stored properties" error later on. |
| } else if (isa<ExtensionDecl>(storage->getDeclContext()) && |
| storage->getBracesRange().isValid()) { |
| readImpl = ReadImplKind::Get; |
| |
| // Otherwise, it's stored. |
| } else { |
| readImpl = ReadImplKind::Stored; |
| } |
| |
| // Prefer using 'set' and 'modify' over a mutable addressor. |
| WriteImplKind writeImpl; |
| ReadWriteImplKind readWriteImpl; |
| if (hasSetter) { |
| writeImpl = WriteImplKind::Set; |
| if (hasModify) { |
| readWriteImpl = ReadWriteImplKind::Modify; |
| } else { |
| readWriteImpl = ReadWriteImplKind::MaterializeToTemporary; |
| } |
| } else if (hasModify) { |
| writeImpl = WriteImplKind::Modify; |
| readWriteImpl = ReadWriteImplKind::Modify; |
| } else if (hasMutableAddress) { |
| writeImpl = WriteImplKind::MutableAddress; |
| readWriteImpl = ReadWriteImplKind::MutableAddress; |
| |
| // Check if we have observers. |
| } else if (readImpl == ReadImplKind::Inherited) { |
| writeImpl = WriteImplKind::InheritedWithObservers; |
| |
| if (hasWillSet) |
| readWriteImpl = ReadWriteImplKind::MaterializeToTemporary; |
| else |
| readWriteImpl = ReadWriteImplKind::InheritedWithDidSet; |
| |
| // Otherwise, it's stored. |
| } else if (readImpl == ReadImplKind::Stored && |
| !cast<VarDecl>(storage)->isLet()) { |
| if (hasWillSet || hasDidSet) { |
| writeImpl = WriteImplKind::StoredWithObservers; |
| |
| if (hasWillSet) |
| readWriteImpl = ReadWriteImplKind::MaterializeToTemporary; |
| else |
| readWriteImpl = ReadWriteImplKind::StoredWithDidSet; |
| } else { |
| writeImpl = WriteImplKind::Stored; |
| readWriteImpl = ReadWriteImplKind::Stored; |
| } |
| |
| // Otherwise, it's immutable. |
| } else { |
| writeImpl = WriteImplKind::Immutable; |
| readWriteImpl = ReadWriteImplKind::Immutable; |
| } |
| |
| StorageImplInfo info(readImpl, writeImpl, readWriteImpl); |
| finishStorageImplInfo(storage, info); |
| |
| return info; |
| } |
| |
| bool SimpleDidSetRequest::evaluate(Evaluator &evaluator, |
| AccessorDecl *decl) const { |
| |
| class OldValueFinder : public ASTWalker { |
| const ParamDecl *OldValueParam; |
| bool foundOldValueRef = false; |
| |
| public: |
| OldValueFinder(const ParamDecl *param) : OldValueParam(param) {} |
| |
| virtual std::pair<bool, Expr *> walkToExprPre(Expr *E) override { |
| if (!E) |
| return {true, E}; |
| if (auto DRE = dyn_cast<DeclRefExpr>(E)) { |
| if (auto decl = DRE->getDecl()) { |
| if (decl == OldValueParam) { |
| foundOldValueRef = true; |
| return {false, nullptr}; |
| } |
| } |
| } |
| |
| return {true, E}; |
| } |
| |
| bool didFindOldValueRef() { return foundOldValueRef; } |
| }; |
| |
| // If this is not a didSet accessor, bail out. |
| if (decl->getAccessorKind() != AccessorKind::DidSet) { |
| return false; |
| } |
| |
| // Always assume non-simple 'didSet' in code completion mode. |
| if (decl->getASTContext().SourceMgr.hasCodeCompletionBuffer()) |
| return false; |
| |
| // didSet must have a single parameter. |
| if (decl->getParameters()->size() != 1) { |
| return false; |
| } |
| |
| auto param = decl->getParameters()->get(0); |
| // If this parameter is not implicit, then it means it has been explicitly |
| // provided by the user (i.e. 'didSet(oldValue)'). This means we cannot |
| // consider this a "simple" didSet because we have to fetch the oldValue |
| // regardless of whether it's referenced in the body or not. |
| if (!param->isImplicit()) { |
| return false; |
| } |
| |
| // If we find a reference to the implicit 'oldValue' parameter, then it is |
| // not a "simple" didSet because we need to fetch it. |
| auto walker = OldValueFinder(param); |
| decl->getTypecheckedBody()->walk(walker); |
| auto hasOldValueRef = walker.didFindOldValueRef(); |
| if (!hasOldValueRef) { |
| // If the body does not refer to implicit 'oldValue', it means we can |
| // consider this as a "simple" didSet. Let's also erase the implicit |
| // oldValue as it is never used. |
| auto &ctx = decl->getASTContext(); |
| decl->setParameters(ParameterList::createEmpty(ctx)); |
| return true; |
| } |
| return false; |
| } |
| |