| //===--- CodeSynthesis.cpp - Type Checking for Declarations ---------------===// |
| // |
| // 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 declarations. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "CodeSynthesis.h" |
| |
| #include "ConstraintSystem.h" |
| #include "TypeChecker.h" |
| #include "TypeCheckObjC.h" |
| #include "TypeCheckType.h" |
| #include "swift/AST/ASTWalker.h" |
| #include "swift/AST/Availability.h" |
| #include "swift/AST/Expr.h" |
| #include "swift/AST/GenericEnvironment.h" |
| #include "swift/AST/GenericSignatureBuilder.h" |
| #include "swift/AST/Initializer.h" |
| #include "swift/AST/ParameterList.h" |
| #include "swift/AST/ProtocolConformance.h" |
| #include "swift/Basic/Defer.h" |
| #include "swift/ClangImporter/ClangModule.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/ADT/StringExtras.h" |
| using namespace swift; |
| |
| const bool IsImplicit = true; |
| |
| /// Should a particular accessor for the given storage be synthesized |
| /// on-demand, or is it always defined eagerly in the file that declared |
| /// the storage? |
| static bool isOnDemandAccessor(AbstractStorageDecl *storage, |
| AccessorKind kind) { |
| assert(kind == AccessorKind::Get || |
| kind == AccessorKind::Set || |
| kind == AccessorKind::Read || |
| kind == AccessorKind::Modify); |
| |
| // If the accessor isn't in the inherent opaque-accessor set of the |
| // declaration, it's on-demand. |
| if (!storage->requiresOpaqueAccessor(kind)) |
| return true; |
| |
| // Currently this only applies to imported declarations because we |
| // eagerly create accessors for all other member storage. |
| // |
| // Note that we can't just use hasClangNode() because the importer |
| // sometimes synthesizes things that lack clang nodes. |
| auto *mod = storage->getDeclContext()->getModuleScopeContext(); |
| return (cast<FileUnit>(mod)->getKind() == FileUnitKind::ClangModule || |
| cast<FileUnit>(mod)->getKind() == FileUnitKind::DWARFModule); |
| } |
| |
| /// 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((isa<AbstractFunctionDecl>(DC) || isa<FileUnit>(DC)) && |
| "Unknown declcontext"); |
| } |
| } |
| |
| static ParamDecl *getParamDeclAtIndex(FuncDecl *fn, unsigned index) { |
| return fn->getParameters()->get(index); |
| } |
| |
| static VarDecl *getFirstParamDecl(FuncDecl *fn) { |
| return getParamDeclAtIndex(fn, 0); |
| }; |
| |
| |
| static ParamDecl *buildArgument(SourceLoc loc, DeclContext *DC, |
| StringRef name, |
| Type interfaceType, |
| VarDecl::Specifier specifier, |
| ASTContext &context) { |
| auto *param = new (context) ParamDecl(specifier, SourceLoc(), SourceLoc(), |
| Identifier(), loc, |
| context.getIdentifier(name), |
| DC); |
| param->setImplicit(); |
| param->setInterfaceType(interfaceType); |
| return param; |
| } |
| |
| /// 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| |
| ParameterList::WithoutTypes); |
| |
| // 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 AccessorDecl *createGetterPrototype(AbstractStorageDecl *storage, |
| ASTContext &ctx) { |
| assert(!storage->getGetter()); |
| |
| SourceLoc loc = storage->getLoc(); |
| |
| GenericEnvironment *genericEnvironmentOfLazyAccessor = nullptr; |
| |
| 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(); |
| auto *bindingInit = cast<PatternBindingInitializer>( |
| bindingDecl->getPatternEntryForVarDecl(varDecl).getInitContext()); |
| |
| selfDecl = bindingInit->getImplicitSelfDecl(); |
| genericEnvironmentOfLazyAccessor = |
| bindingInit->getGenericEnvironmentOfContext(); |
| } |
| } |
| |
| GenericParamList *genericParams = createAccessorGenericParams(storage); |
| |
| // Add an index-forwarding clause. |
| auto *getterParams = buildIndexForwardingParamList(storage, {}, ctx); |
| |
| SourceLoc staticLoc; |
| if (auto var = dyn_cast<VarDecl>(storage)) { |
| if (var->isStatic()) |
| staticLoc = var->getLoc(); |
| } |
| |
| auto storageInterfaceType = storage->getValueInterfaceType(); |
| |
| auto getter = AccessorDecl::create( |
| ctx, loc, /*AccessorKeywordLoc*/ loc, |
| AccessorKind::Get, storage, |
| staticLoc, StaticSpellingKind::None, |
| /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), |
| genericParams, |
| getterParams, |
| TypeLoc::withoutLoc(storageInterfaceType), |
| storage->getDeclContext()); |
| getter->setImplicit(); |
| |
| // 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; |
| |
| // We need to install the generic environment here because: |
| // 1) validating the getter will change the implicit self decl's DC to it, |
| // 2) it's likely that the initializer will be type-checked before the |
| // accessor (and therefore before the normal installation happens), and |
| // 3) type-checking a reference to the self decl will map its type into |
| // its context, which requires an environment to be installed on that |
| // context. |
| // We can safely use the enclosing environment because properties are never |
| // differently generic. |
| if (genericEnvironmentOfLazyAccessor) |
| getter->setGenericEnvironment(genericEnvironmentOfLazyAccessor); |
| |
| if (storage->isGetterMutating()) |
| getter->setSelfAccessKind(SelfAccessKind::Mutating); |
| |
| if (storage->isStatic()) |
| getter->setStatic(); |
| |
| if (!storage->requiresOpaqueAccessor(AccessorKind::Get)) |
| getter->setForcedStaticDispatch(true); |
| |
| // Always add the getter to the context immediately after the storage. |
| addMemberToContextIfNeeded(getter, storage->getDeclContext(), storage); |
| |
| return getter; |
| } |
| |
| static AccessorDecl *createSetterPrototype(AbstractStorageDecl *storage, |
| ASTContext &ctx, |
| AccessorDecl *getter = nullptr) { |
| assert(!storage->getSetter()); |
| assert(storage->supportsMutation()); |
| |
| SourceLoc loc = storage->getLoc(); |
| |
| bool isStatic = storage->isStatic(); |
| bool isMutating = storage->isSetterMutating(); |
| |
| GenericParamList *genericParams = createAccessorGenericParams(storage); |
| |
| // Add a "(value : T, indices...)" argument list. |
| auto storageInterfaceType = storage->getValueInterfaceType(); |
| auto valueDecl = buildArgument(storage->getLoc(), storage->getDeclContext(), |
| "value", storageInterfaceType, |
| VarDecl::Specifier::Default, ctx); |
| auto *params = buildIndexForwardingParamList(storage, valueDecl, ctx); |
| |
| Type setterRetTy = TupleType::getEmpty(ctx); |
| auto setter = AccessorDecl::create( |
| ctx, loc, /*AccessorKeywordLoc*/ SourceLoc(), |
| AccessorKind::Set, storage, |
| /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, |
| /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), |
| genericParams, params, |
| TypeLoc::withoutLoc(setterRetTy), |
| storage->getDeclContext()); |
| setter->setImplicit(); |
| |
| if (isMutating) |
| setter->setSelfAccessKind(SelfAccessKind::Mutating); |
| |
| if (isStatic) |
| setter->setStatic(); |
| |
| // All mutable storage requires a setter. |
| assert(storage->requiresOpaqueAccessor(AccessorKind::Set)); |
| |
| // Always add the setter to the context immediately after the getter. |
| if (!getter) getter = storage->getGetter(); |
| if (!getter) getter = storage->getReadCoroutine(); |
| assert(getter && "always synthesize setter prototype after get/read"); |
| addMemberToContextIfNeeded(setter, storage->getDeclContext(), getter); |
| |
| return setter; |
| } |
| |
| /// 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. |
| static void maybeMarkTransparent(AccessorDecl *accessor, ASTContext &ctx) { |
| auto *DC = accessor->getDeclContext(); |
| auto *nominalDecl = DC->getSelfNominalTypeDecl(); |
| |
| // Global variable accessors are not @_transparent. |
| if (!nominalDecl) |
| return; |
| |
| // Accessors for resilient properties are not @_transparent. |
| if (accessor->getStorage()->isResilient()) |
| return; |
| |
| // Setters for lazy properties are not @_transparent (because the storage |
| // is not ABI-exposed). |
| if (accessor->getStorage()->getAttrs().hasAttribute<LazyAttr>() && |
| accessor->getAccessorKind() == AccessorKind::Set) |
| return; |
| |
| // Accessors for protocol storage requirements are never @_transparent |
| // since they do not have bodies. |
| // |
| // FIXME: Revisit this if we ever get 'real' default implementations. |
| if (isa<ProtocolDecl>(nominalDecl)) |
| return; |
| |
| // 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; |
| |
| // Accessors synthesized on-demand are never transaprent. |
| if (accessor->hasForcedStaticDispatch()) |
| return; |
| |
| accessor->getAttrs().add(new (ctx) TransparentAttr(IsImplicit)); |
| } |
| |
| static AccessorDecl * |
| createCoroutineAccessorPrototype(AbstractStorageDecl *storage, |
| AccessorKind kind, |
| ASTContext &ctx) { |
| assert(kind == AccessorKind::Read || kind == AccessorKind::Modify); |
| |
| SourceLoc loc = storage->getLoc(); |
| |
| bool isStatic = storage->isStatic(); |
| 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 (). |
| 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, TypeLoc::withoutLoc(retTy), dc); |
| accessor->setImplicit(); |
| |
| if (isMutating) |
| accessor->setSelfAccessKind(SelfAccessKind::Mutating); |
| |
| if (isStatic) |
| accessor->setStatic(); |
| |
| // The accessor is final if the storage is. |
| if (storage->isFinal()) |
| makeFinal(ctx, accessor); |
| |
| // 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->getGetter()) { |
| asAvailableAs.push_back(getter); |
| } |
| if (kind == AccessorKind::Modify) { |
| if (FuncDecl *setter = storage->getSetter()) { |
| asAvailableAs.push_back(setter); |
| } |
| } |
| |
| maybeMarkTransparent(accessor, ctx); |
| |
| AvailabilityInference::applyInferredAvailableAttrs(accessor, |
| asAvailableAs, ctx); |
| |
| Decl *afterDecl; |
| if (kind == AccessorKind::Read) { |
| // Add the synthesized read coroutine after the getter, if one exists, |
| // or else immediately after the storage. |
| afterDecl = storage->getGetter(); |
| if (!afterDecl) afterDecl = storage; |
| } else { |
| // Add the synthesized modify coroutine after the setter. |
| afterDecl = storage->getSetter(); |
| } |
| |
| addMemberToContextIfNeeded(accessor, dc, afterDecl); |
| |
| 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); |
| } |
| |
| /// Build an expression that evaluates the specified parameter list as a tuple |
| /// or paren expr, suitable for use in an apply expr. |
| static Expr *buildArgumentForwardingExpr(ArrayRef<ParamDecl*> params, |
| ASTContext &ctx) { |
| SmallVector<Identifier, 4> labels; |
| SmallVector<SourceLoc, 4> labelLocs; |
| SmallVector<Expr *, 4> args; |
| |
| for (auto param : params) { |
| Expr *ref = new (ctx) DeclRefExpr(param, DeclNameLoc(), /*implicit*/ true); |
| if (param->isInOut()) |
| ref = new (ctx) InOutExpr(SourceLoc(), ref, Type(), /*isImplicit=*/true); |
| else if (param->isVariadic()) |
| ref = new (ctx) VarargExpansionExpr(ref, /*implicit*/ true); |
| else if (param->isAutoClosure()) { |
| // If parameter is marked as `@autoclosure` it means |
| // that it has to be called. |
| auto arg = TupleExpr::createEmpty(ctx, SourceLoc(), SourceLoc(), |
| /*implicit=*/true); |
| ref = CallExpr::create(ctx, ref, arg, {}, {}, |
| /*hasTrailingClosure=*/false, |
| /*implicit=*/true); |
| } |
| |
| args.push_back(ref); |
| |
| labels.push_back(param->getArgumentName()); |
| labelLocs.push_back(SourceLoc()); |
| } |
| |
| // A single unlabeled value is not a tuple. |
| if (args.size() == 1 && labels[0].empty() && |
| !isa<VarargExpansionExpr>(args[0])) { |
| return new (ctx) ParenExpr(SourceLoc(), args[0], SourceLoc(), |
| /*hasTrailingClosure=*/false); |
| } |
| |
| return TupleExpr::create(ctx, SourceLoc(), args, labels, labelLocs, |
| SourceLoc(), false, IsImplicit); |
| } |
| |
| |
| /// 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. |
| auto result = buildArgumentForwardingExpr(params, ctx); |
| assert(result && "FIXME: Cannot forward expression"); |
| return result; |
| } |
| |
| enum class SelfAccessorKind { |
| /// We're building a derived accessor on top of whatever this |
| /// class provides. |
| Peer, |
| |
| /// We're building a setter or something around an underlying |
| /// implementation, which might be storage or inherited from a |
| /// superclass. |
| Super, |
| }; |
| |
| static Expr *buildSelfReference(VarDecl *selfDecl, |
| SelfAccessorKind selfAccessorKind, |
| ASTContext &ctx) { |
| switch (selfAccessorKind) { |
| case SelfAccessorKind::Peer: |
| return new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(), IsImplicit); |
| |
| case SelfAccessorKind::Super: |
| return new (ctx) SuperRefExpr(selfDecl, SourceLoc(), IsImplicit); |
| } |
| llvm_unreachable("bad self access kind"); |
| } |
| |
| 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 |
| }; |
| } // end anonymous namespace |
| |
| /// Build an l-value for the storage of a declaration. |
| static Expr *buildStorageReference(AccessorDecl *accessor, |
| AbstractStorageDecl *storage, |
| TargetImpl target, |
| ASTContext &ctx) { |
| AccessSemantics semantics; |
| SelfAccessorKind selfAccessKind; |
| 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; |
| storage = override; |
| |
| // Otherwise do a self-reference, which is dynamically bogus but |
| // should be statically valid. This should only happen in invalid cases. |
| } else { |
| assert(storage->isInvalid()); |
| semantics = AccessSemantics::Ordinary; |
| selfAccessKind = SelfAccessorKind::Peer; |
| } |
| break; |
| } |
| |
| VarDecl *selfDecl = accessor->getImplicitSelfDecl(); |
| if (!selfDecl) { |
| assert(target != TargetImpl::Super); |
| return new (ctx) DeclRefExpr(storage, DeclNameLoc(), IsImplicit, semantics); |
| } |
| |
| Expr *selfDRE = |
| buildSelfReference(selfDecl, selfAccessKind, ctx); |
| |
| if (auto subscript = dyn_cast<SubscriptDecl>(storage)) { |
| Expr *indices = buildSubscriptIndexReference(ctx, accessor); |
| return SubscriptExpr::create(ctx, selfDRE, indices, storage, |
| IsImplicit, semantics); |
| } |
| |
| return new (ctx) MemberRefExpr(selfDRE, SourceLoc(), storage, |
| DeclNameLoc(), IsImplicit, semantics); |
| } |
| |
| /// Load the value of VD. If VD is an @override of another value, we call the |
| /// 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, ctx); |
| } |
| |
| /// Look up the NSCopying protocol from the Foundation module, if present. |
| /// Otherwise return null. |
| static ProtocolDecl *getNSCopyingProtocol(ASTContext &ctx, |
| DeclContext *DC) { |
| auto foundation = ctx.getLoadedModule(ctx.Id_Foundation); |
| if (!foundation) |
| return nullptr; |
| |
| SmallVector<ValueDecl *, 2> results; |
| DC->lookupQualified(foundation, |
| ctx.getSwiftId(KnownFoundationEntity::NSCopying), |
| NL_QualifiedDefault | NL_KnownNonCascadingDependency, |
| results); |
| |
| if (results.size() != 1) |
| return nullptr; |
| |
| return dyn_cast<ProtocolDecl>(results.front()); |
| } |
| |
| static bool checkConformanceToNSCopying(ASTContext &ctx, VarDecl *var, |
| Type type) { |
| auto dc = var->getDeclContext(); |
| auto proto = getNSCopyingProtocol(ctx, dc); |
| |
| if (!proto || !TypeChecker::conformsToProtocol(type, proto, dc, None)) { |
| ctx.Diags.diagnose(var->getLoc(), diag::nscopying_doesnt_conform); |
| return true; |
| } |
| return false; |
| } |
| |
| 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}; |
| } |
| } |
| |
| bool TypeChecker::checkConformanceToNSCopying(VarDecl *var) { |
| Type type = getUnderlyingTypeOfVariable(var).first; |
| return ::checkConformanceToNSCopying(Context, 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. |
| if (checkConformanceToNSCopying(Ctx, VD, underlyingType)) { |
| return Val; |
| } |
| |
| // 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); |
| |
| // Generate: |
| // (force_value_expr type='<null>' |
| // (call_expr type='<null>' |
| // (unresolved_dot_expr type='<null>' field 'copy' |
| // "Val") |
| // (paren_expr type='<null>' |
| // (nil_literal_expr type='<null>')))) |
| auto UDE = new (Ctx) UnresolvedDotExpr(Val, SourceLoc(), |
| Ctx.getIdentifier("copy"), |
| DeclNameLoc(), /*implicit*/true); |
| Expr *Nil = new (Ctx) NilLiteralExpr(SourceLoc(), /*implicit*/true); |
| |
| //- (id)copyWithZone:(NSZone *)zone; |
| Expr *Call = CallExpr::createImplicit(Ctx, UDE, { Nil }, { Ctx.Id_with }); |
| |
| TypeLoc ResultTy; |
| ResultTy.setType(VD->getType()); |
| |
| // If we're working with non-optional types, we're forcing the cast. |
| if (!isOptional) { |
| Call = new (Ctx) ForcedCheckedCastExpr(Call, SourceLoc(), SourceLoc(), |
| TypeLoc::withoutLoc(underlyingType)); |
| Call->setImplicit(); |
| return Call; |
| } |
| |
| // We're working with optional types, so perform a conditional checked |
| // downcast. |
| Call = new (Ctx) ConditionalCheckedCastExpr(Call, SourceLoc(), SourceLoc(), |
| TypeLoc::withoutLoc(underlyingType)); |
| Call->setImplicit(); |
| |
| // Use OptionalEvaluationExpr to evaluate the "?". |
| return new (Ctx) OptionalEvaluationExpr(Call); |
| } |
| |
| /// 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); |
| } |
| |
| // Create: |
| // (assign (decl_ref_expr(VD)), decl_ref_expr(value)) |
| // or: |
| // (assign (member_ref_expr(decl_ref_expr(self), VD)), decl_ref_expr(value)) |
| Expr *dest = buildStorageReference(accessor, storage, target, ctx); |
| |
| body.push_back(new (ctx) AssignExpr(dest, SourceLoc(), value, |
| IsImplicit)); |
| } |
| |
| LLVM_ATTRIBUTE_UNUSED |
| static bool isSynthesizedComputedProperty(AbstractStorageDecl *storage) { |
| return (storage->getAttrs().hasAttribute<LazyAttr>() || |
| storage->getAttrs().hasAttribute<NSManagedAttr>()); |
| } |
| |
| /// 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 void synthesizeTrivialGetterBody(AccessorDecl *getter, |
| TargetImpl target, |
| ASTContext &ctx) { |
| auto storage = getter->getStorage(); |
| assert(!storage->getAttrs().hasAttribute<LazyAttr>() && |
| !storage->getAttrs().hasAttribute<NSManagedAttr>()); |
| |
| SourceLoc loc = storage->getLoc(); |
| |
| Expr *result = |
| createPropertyLoadOrCallSuperclassGetter(getter, storage, target, ctx); |
| ASTNode returnStmt = new (ctx) ReturnStmt(SourceLoc(), result, IsImplicit); |
| |
| getter->setBody(BraceStmt::create(ctx, loc, returnStmt, loc, true)); |
| |
| maybeMarkTransparent(getter, ctx); |
| } |
| |
| /// Synthesize the body of a getter which just directly accesses the |
| /// underlying storage. |
| static void synthesizeTrivialGetterBody(AccessorDecl *getter, |
| ASTContext &ctx) { |
| assert(getter->getStorage()->hasStorage()); |
| synthesizeTrivialGetterBody(getter, TargetImpl::Storage, ctx); |
| } |
| |
| /// Synthesize the body of a getter which just delegates to its superclass |
| /// implementation. |
| static void synthesizeInheritedGetterBody(AccessorDecl *getter, |
| ASTContext &ctx) { |
| // This should call the superclass getter. |
| synthesizeTrivialGetterBody(getter, TargetImpl::Super, ctx); |
| } |
| |
| /// Synthesize the body of a getter which just delegates to an addressor. |
| static void synthesizeAddressedGetterBody(AccessorDecl *getter, |
| ASTContext &ctx) { |
| assert(getter->getStorage()->getAddressor()); |
| |
| // This should call the addressor. |
| synthesizeTrivialGetterBody(getter, TargetImpl::Implementation, ctx); |
| } |
| |
| /// Synthesize the body of a getter which just delegates to a read |
| /// coroutine accessor. |
| static void synthesizeReadCoroutineGetterBody(AccessorDecl *getter, |
| ASTContext &ctx) { |
| assert(getter->getStorage()->getReadCoroutine()); |
| |
| // This should call the read coroutine. |
| synthesizeTrivialGetterBody(getter, TargetImpl::Implementation, ctx); |
| } |
| |
| /// Synthesize the body of a setter which just stores to the given storage |
| /// declaration (which doesn't have to be the storage for the setter). |
| static void |
| synthesizeTrivialSetterBodyWithStorage(AccessorDecl *setter, |
| TargetImpl target, |
| AbstractStorageDecl *storageToUse, |
| ASTContext &ctx) { |
| SourceLoc loc = setter->getStorage()->getLoc(); |
| |
| VarDecl *valueParamDecl = getFirstParamDecl(setter); |
| |
| auto *valueDRE = |
| new (ctx) DeclRefExpr(valueParamDecl, DeclNameLoc(), IsImplicit); |
| SmallVector<ASTNode, 1> setterBody; |
| |
| createPropertyStoreOrCallSuperclassSetter(setter, valueDRE, storageToUse, |
| target, setterBody, ctx); |
| setter->setBody(BraceStmt::create(ctx, loc, setterBody, loc, true)); |
| |
| maybeMarkTransparent(setter, ctx); |
| } |
| |
| static void synthesizeTrivialSetterBody(AccessorDecl *setter, |
| ASTContext &ctx) { |
| auto storage = setter->getStorage(); |
| assert(!isSynthesizedComputedProperty(storage)); |
| synthesizeTrivialSetterBodyWithStorage(setter, TargetImpl::Storage, |
| storage, ctx); |
| } |
| |
| static void synthesizeCoroutineAccessorBody(AccessorDecl *accessor, |
| ASTContext &ctx) { |
| assert(accessor->isCoroutine()); |
| |
| auto storage = accessor->getStorage(); |
| auto target = (accessor->hasForcedStaticDispatch() |
| ? TargetImpl::Ordinary |
| : TargetImpl::Implementation); |
| |
| SourceLoc loc = storage->getLoc(); |
| SmallVector<ASTNode, 1> body; |
| |
| // Build a reference to the storage. |
| Expr *ref = buildStorageReference(accessor, storage, target, ctx); |
| |
| // Wrap it with an `&` marker if this is a modify. |
| if (accessor->getAccessorKind() == AccessorKind::Modify) { |
| ref = new (ctx) InOutExpr(SourceLoc(), ref, Type(), true); |
| } |
| |
| // Yield it. |
| YieldStmt *yield = YieldStmt::create(ctx, loc, loc, ref, loc, true); |
| body.push_back(yield); |
| |
| accessor->setBody(BraceStmt::create(ctx, loc, body, loc, true)); |
| |
| maybeMarkTransparent(accessor, ctx); |
| } |
| |
| /// Synthesize the body of a read coroutine. |
| static void synthesizeReadCoroutineBody(AccessorDecl *read, |
| ASTContext &ctx) { |
| assert(read->getStorage()->getReadImpl() != ReadImplKind::Read); |
| synthesizeCoroutineAccessorBody(read, ctx); |
| } |
| |
| /// Synthesize the body of a modify coroutine. |
| static void synthesizeModifyCoroutineBody(AccessorDecl *modify, |
| ASTContext &ctx) { |
| #ifndef NDEBUG |
| auto impl = modify->getStorage()->getReadWriteImpl(); |
| assert(impl != ReadWriteImplKind::Modify && |
| impl != ReadWriteImplKind::Immutable); |
| #endif |
| synthesizeCoroutineAccessorBody(modify, ctx); |
| } |
| |
| static void addGetterToStorage(AbstractStorageDecl *storage, |
| ASTContext &ctx) { |
| auto getter = createGetterPrototype(storage, ctx); |
| |
| // Install the prototype. |
| storage->setSynthesizedGetter(getter); |
| } |
| |
| static void addSetterToStorage(AbstractStorageDecl *storage, |
| ASTContext &ctx) { |
| auto setter = createSetterPrototype(storage, ctx); |
| |
| // Install the prototype. |
| storage->setSynthesizedSetter(setter); |
| } |
| |
| static void addReadCoroutineToStorage(AbstractStorageDecl *storage, |
| ASTContext &ctx) { |
| auto read = createReadCoroutinePrototype(storage, ctx); |
| |
| // Install the prototype. |
| storage->setSynthesizedReadCoroutine(read); |
| } |
| |
| static void addModifyCoroutineToStorage(AbstractStorageDecl *storage, |
| ASTContext &ctx) { |
| auto modify = createModifyCoroutinePrototype(storage, ctx); |
| |
| // Install the prototype. |
| storage->setSynthesizedModifyCoroutine(modify); |
| } |
| |
| |
| static void addOpaqueAccessorToStorage(AbstractStorageDecl *storage, |
| AccessorKind kind, |
| ASTContext &ctx) { |
| switch (kind) { |
| case AccessorKind::Get: |
| return addGetterToStorage(storage, ctx); |
| |
| case AccessorKind::Set: |
| return addSetterToStorage(storage, ctx); |
| |
| case AccessorKind::Read: |
| return addReadCoroutineToStorage(storage, ctx); |
| |
| case AccessorKind::Modify: |
| return addModifyCoroutineToStorage(storage, ctx); |
| |
| #define OPAQUE_ACCESSOR(ID, KEYWORD) |
| #define ACCESSOR(ID) \ |
| case AccessorKind::ID: |
| #include "swift/AST/AccessorKinds.def" |
| llvm_unreachable("not an opaque accessor"); |
| } |
| } |
| |
| static void addExpectedOpaqueAccessorsToStorage(AbstractStorageDecl *storage, |
| ASTContext &ctx) { |
| // 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 (storage->getBaseName().empty()) |
| return; |
| storage->visitExpectedOpaqueAccessors([&](AccessorKind kind) { |
| // If the accessor is already present, there's nothing to do. |
| if (storage->getAccessor(kind)) |
| return; |
| |
| addOpaqueAccessorToStorage(storage, kind, ctx); |
| }); |
| } |
| |
| /// Add trivial accessors to a Stored or Addressed property. |
| static void addTrivialAccessorsToStorage(AbstractStorageDecl *storage, |
| ASTContext &ctx) { |
| assert(!isSynthesizedComputedProperty(storage)); |
| addExpectedOpaqueAccessorsToStorage(storage, ctx); |
| } |
| |
| static StorageImplInfo getProtocolStorageImpl(AbstractStorageDecl *storage) { |
| auto protocol = cast<ProtocolDecl>(storage->getDeclContext()); |
| if (protocol->isObjC()) { |
| return StorageImplInfo::getComputed(storage->supportsMutation()); |
| } else { |
| return StorageImplInfo::getOpaque(storage->supportsMutation(), |
| storage->getOpaqueReadOwnership()); |
| } |
| } |
| |
| /// Given a storage declaration in a protocol, set it up with the right |
| /// StorageImpl and add the right set of opaque accessors. |
| static void setProtocolStorageImpl(AbstractStorageDecl *storage, |
| ASTContext &ctx) { |
| addExpectedOpaqueAccessorsToStorage(storage, ctx); |
| |
| storage->overwriteImplInfo(getProtocolStorageImpl(storage)); |
| } |
| |
| /// Synthesize the body of a setter which just delegates to a mutable |
| /// addressor. |
| static void synthesizeMutableAddressSetterBody(AccessorDecl *setter, |
| ASTContext &ctx) { |
| // This should call the mutable addressor. |
| synthesizeTrivialSetterBodyWithStorage(setter, TargetImpl::Implementation, |
| setter->getStorage(), ctx); |
| } |
| |
| /// Synthesize the body of a setter which just delegates to a modify |
| /// coroutine accessor. |
| static void synthesizeModifyCoroutineSetterBody(AccessorDecl *setter, |
| ASTContext &ctx) { |
| // This should call the modify coroutine. |
| synthesizeTrivialSetterBodyWithStorage(setter, TargetImpl::Implementation, |
| setter->getStorage(), ctx); |
| } |
| |
| static void convertNSManagedStoredVarToComputed(VarDecl *VD, ASTContext &ctx) { |
| // If it's not still stored, just bail out. |
| if (!VD->getImplInfo().isSimpleStored()) |
| return; |
| |
| // We might already have synthesized the getter and setter declarations |
| // from e.g. type-checking a conformance, or just from an invalid earlier |
| // declaration. |
| |
| // Creating these this way will not trigger synthesis of implementations |
| // because of the NSManaged attribute. |
| |
| // Create the getter. |
| if (!VD->getGetter()) { |
| addGetterToStorage(VD, ctx); |
| } |
| |
| // Create the setter. |
| if (!VD->getSetter()) { |
| addSetterToStorage(VD, ctx); |
| } |
| |
| // Okay, we have both a getter and setter; overwrite the impl info. |
| VD->overwriteImplInfo(StorageImplInfo::getMutableComputed()); |
| |
| addExpectedOpaqueAccessorsToStorage(VD, ctx); |
| } |
| |
| void synthesizeAccessorBody(AbstractFunctionDecl *fn, void *); |
| |
| /// The specified AbstractStorageDecl was just found to satisfy a |
| /// protocol property requirement. Ensure that it has the full |
| /// complement of accessors. |
| void TypeChecker::synthesizeWitnessAccessorsForStorage( |
| AbstractStorageDecl *requirement, |
| AbstractStorageDecl *storage) { |
| bool addedAccessor = false; |
| |
| requirement->visitExpectedOpaqueAccessors([&](AccessorKind kind) { |
| // If the accessor already exists, we have nothing to do. |
| if (storage->getAccessor(kind)) |
| return; |
| |
| // Otherwise, synthesize it. |
| addOpaqueAccessorToStorage(storage, kind, Context); |
| |
| // Flag that we've added an accessor. |
| addedAccessor = true; |
| |
| // Trigger synthesize of the accessor body if it's created on-demand. |
| if (isOnDemandAccessor(storage, kind)) { |
| auto *accessor = storage->getAccessor(kind); |
| assert(!accessor->hasBody()); |
| accessor->setBodySynthesizer(&synthesizeAccessorBody); |
| |
| // Make sure SILGen emits the accessor; on-demand accessors have shared |
| // linkage, and if its defined in a different translation unit from the |
| // conformance we cannot simply generate an external declaration. |
| Context.addExternalDecl(accessor); |
| DeclsToFinalize.insert(accessor); |
| } |
| }); |
| |
| // Cue (delayed) validation of any accessors we just added, just |
| // in case this is coming after the normal delayed validation finished. |
| if (addedAccessor) { |
| DeclsToFinalize.insert(storage); |
| } |
| } |
| |
| /// Given a VarDecl with a willSet: and/or didSet: specifier, synthesize the |
| /// setter which calls them. |
| static void synthesizeObservedSetterBody(AccessorDecl *Set, |
| TargetImpl target, |
| ASTContext &Ctx) { |
| auto VD = cast<VarDecl>(Set->getStorage()); |
| |
| SourceLoc Loc = VD->getLoc(); |
| |
| // We have to be paranoid about the accessors already having bodies |
| // because there might be an (invalid) existing definition. |
| |
| // Okay, the getter is done, create the setter now. Start by finding the |
| // decls for 'self' and 'value'. |
| auto *SelfDecl = Set->getImplicitSelfDecl(); |
| VarDecl *ValueDecl = Set->getParameters()->get(0); |
| |
| // 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; |
| |
| // If there is a didSet, it will take the old value. Load it into a temporary |
| // 'let' so we have it for later. |
| // TODO: check the body of didSet to only do this load (which may call the |
| // superclass getter) if didSet takes an argument. |
| VarDecl *OldValue = nullptr; |
| if (VD->getDidSetFunc()) { |
| Expr *OldValueExpr |
| = createPropertyLoadOrCallSuperclassGetter(Set, VD, target, Ctx); |
| |
| OldValue = new (Ctx) VarDecl(/*IsStatic*/false, VarDecl::Specifier::Let, |
| /*IsCaptureList*/false, SourceLoc(), |
| Ctx.getIdentifier("tmp"), Set); |
| OldValue->setImplicit(); |
| auto *tmpPattern = new (Ctx) NamedPattern(OldValue, /*implicit*/ true); |
| auto *tmpPBD = PatternBindingDecl::createImplicit( |
| Ctx, StaticSpellingKind::None, tmpPattern, OldValueExpr, Set); |
| SetterBody.push_back(tmpPBD); |
| SetterBody.push_back(OldValue); |
| } |
| |
| // Create: |
| // (call_expr (dot_syntax_call_expr (decl_ref_expr(willSet)), |
| // (decl_ref_expr(self))), |
| // (declrefexpr(value))) |
| // or: |
| // (call_expr (decl_ref_expr(willSet)), (declrefexpr(value))) |
| if (auto willSet = VD->getWillSetFunc()) { |
| Expr *Callee = new (Ctx) DeclRefExpr(willSet, DeclNameLoc(), /*imp*/true); |
| auto *ValueDRE = new (Ctx) DeclRefExpr(ValueDecl, DeclNameLoc(), |
| /*imp*/true); |
| if (SelfDecl) { |
| auto *SelfDRE = new (Ctx) DeclRefExpr(SelfDecl, DeclNameLoc(), |
| /*imp*/true); |
| Callee = new (Ctx) DotSyntaxCallExpr(Callee, SourceLoc(), SelfDRE); |
| } |
| SetterBody.push_back(CallExpr::createImplicit(Ctx, Callee, { ValueDRE }, |
| { Identifier() })); |
| } |
| |
| // Create an assignment into the storage or call to superclass setter. |
| auto *ValueDRE = new (Ctx) DeclRefExpr(ValueDecl, DeclNameLoc(), true); |
| createPropertyStoreOrCallSuperclassSetter(Set, ValueDRE, VD, target, |
| SetterBody, Ctx); |
| |
| // Create: |
| // (call_expr (dot_syntax_call_expr (decl_ref_expr(didSet)), |
| // (decl_ref_expr(self))), |
| // (decl_ref_expr(tmp))) |
| // or: |
| // (call_expr (decl_ref_expr(didSet)), (decl_ref_expr(tmp))) |
| if (auto didSet = VD->getDidSetFunc()) { |
| auto *OldValueExpr = new (Ctx) DeclRefExpr(OldValue, DeclNameLoc(), |
| /*impl*/true); |
| Expr *Callee = new (Ctx) DeclRefExpr(didSet, DeclNameLoc(), /*imp*/true); |
| if (SelfDecl) { |
| auto *SelfDRE = new (Ctx) DeclRefExpr(SelfDecl, DeclNameLoc(), |
| /*imp*/true); |
| Callee = new (Ctx) DotSyntaxCallExpr(Callee, SourceLoc(), SelfDRE); |
| } |
| SetterBody.push_back(CallExpr::createImplicit(Ctx, Callee, { OldValueExpr }, |
| { Identifier() })); |
| } |
| |
| Set->setBody(BraceStmt::create(Ctx, Loc, SetterBody, Loc, true)); |
| } |
| |
| static void synthesizeStoredWithObserversSetterBody(AccessorDecl *setter, |
| ASTContext &ctx) { |
| synthesizeObservedSetterBody(setter, TargetImpl::Storage, ctx); |
| } |
| |
| static void synthesizeInheritedWithObserversSetterBody(AccessorDecl *setter, |
| ASTContext &ctx) { |
| synthesizeObservedSetterBody(setter, TargetImpl::Super, 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 void synthesizeLazyGetterBody(AbstractFunctionDecl *fn, void *context) { |
| auto &Ctx = fn->getASTContext(); |
| |
| // FIXME: Remove TypeChecker dependencies below. |
| auto &TC = *(TypeChecker *) Ctx.getLazyResolver(); |
| |
| // The stored property backing the lazy var. |
| AccessorDecl *Get = cast<AccessorDecl>(fn); |
| VarDecl *Storage = (VarDecl *) context; |
| |
| // The lazy var itself. |
| auto VD = cast<VarDecl>(Get->getStorage()); |
| |
| if (Get->isInvalid() || Ctx.hadError()) |
| return; |
| |
| // The getter checks the optional, storing the initial value in if nil. The |
| // specific pattern we generate is: |
| // get { |
| // let tmp1 = storage |
| // if tmp1 { |
| // 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::Specifier::Let, |
| /*IsCaptureList*/false, SourceLoc(), |
| Ctx.getIdentifier("tmp1"), Get); |
| Tmp1VD->setImplicit(); |
| |
| auto *Tmp1PBDPattern = new (Ctx) NamedPattern(Tmp1VD, /*implicit*/true); |
| auto *Tmp1Init = |
| createPropertyLoadOrCallSuperclassGetter(Get, Storage, |
| TargetImpl::Storage, Ctx); |
| auto *Tmp1PBD = PatternBindingDecl::createImplicit( |
| Ctx, StaticSpellingKind::None, Tmp1PBDPattern, Tmp1Init, Get); |
| Body.push_back(Tmp1PBD); |
| Body.push_back(Tmp1VD); |
| |
| // Build the early return inside the if. |
| auto *Tmp1DRE = new (Ctx) DeclRefExpr(Tmp1VD, DeclNameLoc(), /*Implicit*/true, |
| AccessSemantics::DirectToStorage); |
| auto *EarlyReturnVal = new (Ctx) ForceValueExpr(Tmp1DRE, SourceLoc()); |
| auto *Return = new (Ctx) ReturnStmt(SourceLoc(), EarlyReturnVal, |
| /*implicit*/true); |
| |
| // Build the "if" around the early return. |
| Tmp1DRE = new (Ctx) DeclRefExpr(Tmp1VD, DeclNameLoc(), /*Implicit*/true, |
| AccessSemantics::DirectToStorage); |
| |
| // Call through "hasValue" on the decl ref. |
| Tmp1DRE->setType(OptionalType::get(VD->getType())); |
| constraints::ConstraintSystem cs(TC, |
| VD->getDeclContext(), |
| constraints::ConstraintSystemOptions()); |
| constraints::Solution solution(cs, constraints::Score()); |
| auto HasValueExpr = solution.convertOptionalToBool(Tmp1DRE, nullptr); |
| |
| Body.push_back(new (Ctx) IfStmt(SourceLoc(), HasValueExpr, Return, |
| /*elseloc*/SourceLoc(), /*else*/nullptr, |
| /*implicit*/ true, Ctx)); |
| |
| |
| auto *Tmp2VD = new (Ctx) VarDecl(/*IsStatic*/false, VarDecl::Specifier::Let, |
| /*IsCaptureList*/false, SourceLoc(), |
| Ctx.getIdentifier("tmp2"), |
| Get); |
| Tmp2VD->setType(VD->getType()); |
| Tmp2VD->setInterfaceType(VD->getInterfaceType()); |
| 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 *InitValue = VD->getParentInitializer(); |
| auto PBD = VD->getParentPatternBinding(); |
| unsigned entryIndex = PBD->getPatternEntryIndexForVarDecl(VD); |
| assert(PBD->isInitializerLazy(entryIndex)); |
| bool wasInitializerChecked = PBD->isInitializerChecked(entryIndex); |
| PBD->setInitializerChecked(entryIndex); |
| |
| // 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 problems with |
| // re-typechecking it if it was already type-checked. |
| // FIXME: we should really have stronger invariants than this. Leaving it |
| // unwrapped may expose both expressions to naive walkers |
| if (wasInitializerChecked) { |
| auto initType = InitValue->getType(); |
| InitValue = new (Ctx) LazyInitializerExpr(InitValue); |
| InitValue->setType(initType); |
| } |
| |
| Pattern *Tmp2PBDPattern = new (Ctx) NamedPattern(Tmp2VD, /*implicit*/true); |
| Tmp2PBDPattern = |
| TypedPattern::createImplicit(Ctx, Tmp2PBDPattern, VD->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); |
| createPropertyStoreOrCallSuperclassSetter(Get, Tmp2DRE, Storage, |
| TargetImpl::Storage, Body, Ctx); |
| |
| // Return tmp2. |
| Tmp2DRE = new (Ctx) DeclRefExpr(Tmp2VD, DeclNameLoc(), /*Implicit*/true, |
| AccessSemantics::DirectToStorage); |
| |
| Body.push_back(new (Ctx) ReturnStmt(SourceLoc(), Tmp2DRE, /*implicit*/true)); |
| |
| Get->setBody(BraceStmt::create(Ctx, VD->getLoc(), Body, VD->getLoc(), |
| /*implicit*/true)); |
| } |
| |
| static void synthesizeLazySetterBody(AbstractFunctionDecl *fn, void *context) { |
| auto *setter = cast<AccessorDecl>(fn); |
| auto *underlyingStorage = (VarDecl *) context; |
| auto &ctx = setter->getASTContext(); |
| |
| if (setter->isInvalid() || ctx.hadError()) |
| return; |
| |
| synthesizeTrivialSetterBodyWithStorage(setter, TargetImpl::Storage, |
| underlyingStorage, ctx); |
| } |
| |
| void swift::completeLazyVarImplementation(VarDecl *VD) { |
| auto &Context = VD->getASTContext(); |
| |
| assert(VD->getAttrs().hasAttribute<LazyAttr>()); |
| assert(VD->getReadImpl() == ReadImplKind::Get); |
| assert(VD->getWriteImpl() == WriteImplKind::Set); |
| assert(!VD->isStatic() && "Static vars are already lazy on their own"); |
| |
| // 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 StorageTy = OptionalType::get(VD->getType()); |
| auto StorageInterfaceTy = OptionalType::get(VD->getInterfaceType()); |
| |
| auto *Storage = new (Context) VarDecl(/*IsStatic*/false, VarDecl::Specifier::Var, |
| /*IsCaptureList*/false, VD->getLoc(), |
| StorageName, |
| VD->getDeclContext()); |
| Storage->setInterfaceType(StorageInterfaceTy); |
| Storage->setUserAccessible(false); |
| addMemberToContextIfNeeded(Storage, VD->getDeclContext(), VD); |
| |
| // Create the pattern binding decl for the storage decl. This will get |
| // default initialized to nil. |
| Pattern *PBDPattern = new (Context) NamedPattern(Storage, /*implicit*/true); |
| PBDPattern = TypedPattern::createImplicit(Context, PBDPattern, StorageTy); |
| auto *PBD = PatternBindingDecl::createImplicit( |
| Context, StaticSpellingKind::None, PBDPattern, /*init*/ nullptr, |
| VD->getDeclContext(), /*VarLoc*/ VD->getLoc()); |
| addMemberToContextIfNeeded(PBD, VD->getDeclContext(), VD); |
| |
| // Now that we've got the storage squared away, enqueue the getter and |
| // setter to be synthesized. |
| VD->getGetter()->setBodySynthesizer(&synthesizeLazyGetterBody, Storage); |
| VD->getSetter()->setBodySynthesizer(&synthesizeLazySetterBody, Storage); |
| |
| // Mark the vardecl to be final, implicit, and private. In a class, this |
| // prevents it from being dynamically dispatched. Note that we do this after |
| // the accessors are set up, because we don't want the setter for the lazy |
| // property to inherit these properties from the storage. |
| if (VD->getDeclContext()->getSelfClassDecl()) |
| makeFinal(Context, Storage); |
| Storage->setImplicit(); |
| Storage->overwriteAccess(AccessLevel::Private); |
| Storage->overwriteSetterAccess(AccessLevel::Private); |
| } |
| |
| static bool wouldBeCircularSynthesis(AbstractStorageDecl *storage, |
| AccessorKind kind) { |
| switch (kind) { |
| case AccessorKind::Get: |
| return storage->getReadImpl() == ReadImplKind::Get; |
| case AccessorKind::Read: |
| return storage->getReadImpl() == ReadImplKind::Read; |
| case AccessorKind::Set: |
| return storage->getWriteImpl() == WriteImplKind::Set; |
| case AccessorKind::Modify: |
| return storage->getReadWriteImpl() == ReadWriteImplKind::Modify; |
| #define OPAQUE_ACCESSOR(ID, KEYWORD) |
| #define ACCESSOR(ID) \ |
| case AccessorKind::ID: |
| #include "swift/AST/AccessorKinds.def" |
| llvm_unreachable("unexpected opaque accessor"); |
| } |
| llvm_unreachable("bad kind"); |
| } |
| |
| void swift::triggerAccessorSynthesis(TypeChecker &TC, |
| AbstractStorageDecl *storage) { |
| auto VD = dyn_cast<VarDecl>(storage); |
| maybeAddAccessorsToStorage(storage); |
| |
| // Synthesize accessors for lazy, all checking already been performed. |
| bool lazy = false; |
| if (VD && VD->getAttrs().hasAttribute<LazyAttr>() && !VD->isStatic() && |
| !VD->getGetter()->hasBody()) { |
| completeLazyVarImplementation(VD); |
| lazy = true; |
| } |
| |
| // Trigger accessor synthesis. |
| storage->visitExpectedOpaqueAccessors([&](AccessorKind kind) { |
| // Ignore 'get' and 'set' for variables that we triggered above. |
| // TODO: just record the lazy-storage link in the AST, don't trigger |
| // in completeLazyVarImplementation, and remove this special case. |
| if (lazy && (kind == AccessorKind::Get || kind == AccessorKind::Set)) |
| return; |
| |
| // Don't synthesize an accessor if the accessor is supposed to be |
| // the basis of the storage implementation. |
| if (wouldBeCircularSynthesis(storage, kind)) |
| return; |
| |
| // Don't try to synthesize an accessor that doesn't exist. |
| // TODO: should this be an assertion? |
| auto accessor = storage->getAccessor(kind); |
| if (!accessor) |
| return; |
| |
| accessor->setBodySynthesizer(&synthesizeAccessorBody); |
| |
| TC.Context.addSynthesizedDecl(accessor); |
| TC.DeclsToFinalize.insert(accessor); |
| }); |
| } |
| |
| static void maybeAddAccessorsToLazyVariable(VarDecl *var, ASTContext &ctx) { |
| // If there are already accessors, something is invalid; bail out. |
| if (!var->getImplInfo().isSimpleStored()) |
| return; |
| |
| if (!var->getGetter()) { |
| addGetterToStorage(var, ctx); |
| } |
| |
| if (!var->getSetter()) { |
| addSetterToStorage(var, ctx); |
| } |
| |
| var->overwriteImplInfo(StorageImplInfo::getMutableComputed()); |
| |
| addExpectedOpaqueAccessorsToStorage(var, ctx); |
| } |
| |
| /// Try to add the appropriate accessors required a storage declaration. |
| /// This needs to be idempotent. |
| /// |
| /// Note that the parser synthesizes accessors in some cases: |
| /// - it synthesizes a getter and setter for an observing property |
| /// - it synthesizes a setter for get+mutableAddress |
| void swift::maybeAddAccessorsToStorage(AbstractStorageDecl *storage) { |
| auto &ctx = storage->getASTContext(); |
| |
| // Lazy properties require special handling. |
| if (storage->getAttrs().hasAttribute<LazyAttr>()) { |
| maybeAddAccessorsToLazyVariable(cast<VarDecl>(storage), ctx); |
| return; |
| } |
| |
| auto *dc = storage->getDeclContext(); |
| |
| // Local variables don't otherwise get accessors. |
| if (dc->isLocalContext()) |
| return; |
| |
| // Implicit properties don't get accessors. |
| if (storage->isImplicit()) |
| return; |
| |
| if (!dc->isTypeContext()) { |
| // dynamic globals need accessors. |
| if (dc->isModuleScopeContext() && storage->isNativeDynamic()) { |
| addTrivialAccessorsToStorage(storage, ctx); |
| return; |
| } |
| // Fixed-layout global variables don't get accessors. |
| if (!storage->isResilient()) |
| return; |
| |
| // In a protocol context, variables written as just "var x : Int" or |
| // "let x : Int" are errors and recovered by building a computed property |
| // with just a getter. Diagnose this and create the getter decl now. |
| } else if (isa<ProtocolDecl>(dc)) { |
| if (storage->hasStorage()) { |
| auto var = cast<VarDecl>(storage); |
| |
| if (var->isLet()) { |
| ctx.Diags.diagnose(var->getLoc(), |
| diag::protocol_property_must_be_computed_var) |
| .fixItReplace(var->getParentPatternBinding()->getLoc(), "var") |
| .fixItInsertAfter(var->getTypeLoc().getLoc(), " { get }"); |
| } else { |
| auto diag = ctx.Diags.diagnose(var->getLoc(), |
| diag::protocol_property_must_be_computed); |
| auto braces = var->getBracesRange(); |
| |
| if (braces.isValid()) |
| diag.fixItReplace(braces, "{ get <#set#> }"); |
| else |
| diag.fixItInsertAfter(var->getTypeLoc().getLoc(), " { get <#set#> }"); |
| } |
| } |
| |
| setProtocolStorageImpl(storage, ctx); |
| return; |
| |
| // NSManaged properties on classes require special handling. |
| } else if (dc->getSelfClassDecl()) { |
| auto var = dyn_cast<VarDecl>(storage); |
| if (var && var->getAttrs().hasAttribute<NSManagedAttr>()) { |
| convertNSManagedStoredVarToComputed(var, ctx); |
| return; |
| } |
| |
| // Stored properties imported from Clang don't get accessors. |
| } else if (auto *structDecl = dyn_cast<StructDecl>(dc)) { |
| if (structDecl->hasClangNode()) |
| return; |
| } |
| |
| // 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 (storage->getGetter()) { |
| addExpectedOpaqueAccessorsToStorage(storage, ctx); |
| } |
| return; |
| } |
| |
| // Everything else gets mandatory accessors. |
| addTrivialAccessorsToStorage(storage, ctx); |
| } |
| |
| static void synthesizeGetterBody(AccessorDecl *getter, |
| ASTContext &ctx) { |
| if (getter->hasForcedStaticDispatch()) { |
| synthesizeTrivialGetterBody(getter, TargetImpl::Ordinary, ctx); |
| return; |
| } |
| |
| switch (getter->getStorage()->getReadImpl()) { |
| case ReadImplKind::Stored: |
| synthesizeTrivialGetterBody(getter, ctx); |
| return; |
| |
| case ReadImplKind::Get: |
| llvm_unreachable("synthesizing getter that already exists?"); |
| |
| case ReadImplKind::Inherited: |
| synthesizeInheritedGetterBody(getter, ctx); |
| return; |
| |
| case ReadImplKind::Address: |
| synthesizeAddressedGetterBody(getter, ctx); |
| return; |
| |
| case ReadImplKind::Read: |
| synthesizeReadCoroutineGetterBody(getter, ctx); |
| return; |
| } |
| llvm_unreachable("bad ReadImplKind"); |
| } |
| |
| static void synthesizeSetterBody(AccessorDecl *setter, |
| ASTContext &ctx) { |
| switch (setter->getStorage()->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: |
| llvm_unreachable("synthesizing setter for unknown reason?"); |
| |
| case WriteImplKind::MutableAddress: |
| return synthesizeMutableAddressSetterBody(setter, ctx); |
| |
| case WriteImplKind::Modify: |
| return synthesizeModifyCoroutineSetterBody(setter, ctx); |
| } |
| llvm_unreachable("bad ReadImplKind"); |
| } |
| |
| void synthesizeAccessorBody(AbstractFunctionDecl *fn, void *) { |
| auto *accessor = cast<AccessorDecl>(fn); |
| auto &ctx = accessor->getASTContext(); |
| |
| if (accessor->isInvalid() || ctx.hadError()) |
| return; |
| |
| switch (accessor->getAccessorKind()) { |
| case AccessorKind::Get: |
| synthesizeGetterBody(accessor, ctx); |
| return; |
| |
| case AccessorKind::Set: |
| synthesizeSetterBody(accessor, ctx); |
| return; |
| |
| case AccessorKind::Read: |
| synthesizeReadCoroutineBody(accessor, ctx); |
| return; |
| |
| case AccessorKind::Modify: |
| synthesizeModifyCoroutineBody(accessor, ctx); |
| return; |
| |
| case AccessorKind::WillSet: |
| case AccessorKind::DidSet: |
| case AccessorKind::Address: |
| case AccessorKind::MutableAddress: |
| break; |
| } |
| llvm_unreachable("bad synthesized function kind"); |
| } |
| |
| static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var, |
| SmallVectorImpl<DefaultArgumentInitializer *> &defaultInits, |
| unsigned paramSize, ASTContext &ctx) { |
| // First and foremost, if this is a constant don't bother. |
| if (var->isLet()) |
| return; |
| |
| // We can only provide default values for patterns binding a single variable. |
| // i.e. var (a, b) = getSomeTuple() is not allowed. |
| if (!var->getParentPattern()->getSingleVar()) |
| return; |
| |
| // If we don't have an expression initializer or silgen can't assign a default |
| // initializer, then we can't generate a default value. An example of where |
| // silgen can assign a default is var x: Int? where the default is nil. |
| // If the variable is lazy, go ahead and give it a default value. |
| if (!var->getAttrs().hasAttribute<LazyAttr>() && |
| !var->getParentPatternBinding()->isDefaultInitializable()) |
| return; |
| |
| // We can add a default value now. |
| |
| // Give this some bogus context right now, we'll fix it after making |
| // the constructor. |
| auto *initDC = new (ctx) DefaultArgumentInitializer( |
| arg->getDeclContext(), paramSize); |
| |
| defaultInits.push_back(initDC); |
| |
| // If the variable has a type T? and no initial value, return a nil literal |
| // default arg. All lazy variables return a nil literal as well. *Note* that |
| // the type will always be a sugared T? because we don't default init an |
| // explicit Optional<T>. |
| if ((isa<OptionalType>(var->getValueInterfaceType().getPointer()) && |
| !var->getParentInitializer()) || |
| var->getAttrs().hasAttribute<LazyAttr>()) { |
| arg->setDefaultArgumentKind(DefaultArgumentKind::NilLiteral); |
| return; |
| } |
| |
| // Set the default value to the variable. When we emit this in silgen |
| // we're going to call the variable's initializer expression. |
| arg->setStoredProperty(var); |
| arg->setDefaultArgumentKind(DefaultArgumentKind::StoredProperty); |
| } |
| |
| /// Create an implicit struct or class constructor. |
| /// |
| /// \param decl The struct or class for which a constructor will be created. |
| /// \param ICK The kind of implicit constructor to create. |
| /// |
| /// \returns The newly-created constructor, which has already been type-checked |
| /// (but has not been added to the containing struct or class). |
| ConstructorDecl *swift::createImplicitConstructor(TypeChecker &tc, |
| NominalTypeDecl *decl, |
| ImplicitConstructorKind ICK) { |
| assert(!decl->hasClangNode()); |
| |
| ASTContext &ctx = tc.Context; |
| SourceLoc Loc = decl->getLoc(); |
| auto accessLevel = AccessLevel::Internal; |
| |
| // Determine the parameter type of the implicit constructor. |
| SmallVector<ParamDecl*, 8> params; |
| SmallVector<DefaultArgumentInitializer *, 8> defaultInits; |
| if (ICK == ImplicitConstructorKind::Memberwise) { |
| assert(isa<StructDecl>(decl) && "Only struct have memberwise constructor"); |
| |
| for (auto member : decl->getMembers()) { |
| auto var = dyn_cast<VarDecl>(member); |
| if (!var) |
| continue; |
| |
| // Implicit, computed, and static properties are not initialized. |
| // The exception is lazy properties, which due to batch mode we may or |
| // may not have yet finalized, so they may currently be "stored" or |
| // "computed" in the current AST state. |
| if (var->isImplicit() || var->isStatic()) |
| continue; |
| |
| if (!var->hasStorage() && !var->getAttrs().hasAttribute<LazyAttr>()) |
| continue; |
| |
| // Initialized 'let' properties have storage, but don't get an argument |
| // to the memberwise initializer since they already have an initial |
| // value that cannot be overridden. |
| if (var->isLet() && var->getParentInitializer()) |
| continue; |
| |
| accessLevel = std::min(accessLevel, var->getFormalAccess()); |
| |
| tc.validateDecl(var); |
| auto varInterfaceType = var->getValueInterfaceType(); |
| |
| // If var is a lazy property, its value is provided for the underlying |
| // storage. We thus take an optional of the properties type. We only |
| // need to do this because the implicit constructor is added before all |
| // the properties are type checked. Perhaps init() synth should be moved |
| // later. |
| if (var->getAttrs().hasAttribute<LazyAttr>()) |
| varInterfaceType = OptionalType::get(varInterfaceType); |
| |
| // Create the parameter. |
| auto *arg = new (ctx) |
| ParamDecl(VarDecl::Specifier::Default, SourceLoc(), Loc, |
| var->getName(), Loc, var->getName(), decl); |
| arg->setInterfaceType(varInterfaceType); |
| arg->setImplicit(); |
| |
| maybeAddMemberwiseDefaultArg(arg, var, defaultInits, params.size(), ctx); |
| |
| params.push_back(arg); |
| } |
| } |
| |
| auto paramList = ParameterList::create(ctx, params); |
| |
| // Create the constructor. |
| DeclName name(ctx, DeclBaseName::createConstructor(), paramList); |
| auto *ctor = |
| new (ctx) ConstructorDecl(name, Loc, |
| OTK_None, /*FailabilityLoc=*/SourceLoc(), |
| /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), |
| paramList, /*GenericParams=*/nullptr, decl); |
| |
| // Mark implicit. |
| ctor->setImplicit(); |
| ctor->setAccess(accessLevel); |
| |
| if (ICK == ImplicitConstructorKind::Memberwise) { |
| ctor->setIsMemberwiseInitializer(); |
| |
| // Fix default argument init contexts now that we have a constructor. |
| for (auto initDC : defaultInits) { |
| initDC->changeFunction(ctor, paramList); |
| } |
| } |
| |
| // If we are defining a default initializer for a class that has a superclass, |
| // it overrides the default initializer of its superclass. Add an implicit |
| // 'override' attribute. |
| if (auto classDecl = dyn_cast<ClassDecl>(decl)) { |
| if (classDecl->getSuperclass()) |
| ctor->getAttrs().add(new (ctx) OverrideAttr(/*IsImplicit=*/true)); |
| } |
| |
| return ctor; |
| } |
| |
| /// Create a stub body that emits a fatal error message. |
| static void synthesizeStubBody(AbstractFunctionDecl *fn, void *) { |
| auto *ctor = cast<ConstructorDecl>(fn); |
| auto &ctx = ctor->getASTContext(); |
| |
| auto unimplementedInitDecl = ctx.getUnimplementedInitializerDecl(); |
| auto classDecl = ctor->getDeclContext()->getSelfClassDecl(); |
| if (!unimplementedInitDecl) { |
| ctx.Diags.diagnose(classDecl->getLoc(), |
| diag::missing_unimplemented_init_runtime); |
| return; |
| } |
| |
| // Create a call to Swift._unimplementedInitializer |
| auto loc = classDecl->getLoc(); |
| Expr *ref = new (ctx) DeclRefExpr(unimplementedInitDecl, |
| DeclNameLoc(loc), |
| /*Implicit=*/true); |
| |
| llvm::SmallString<64> buffer; |
| StringRef fullClassName = ctx.AllocateCopy( |
| (classDecl->getModuleContext()->getName().str() + |
| "." + |
| classDecl->getName().str()).toStringRef(buffer)); |
| |
| Expr *className = new (ctx) StringLiteralExpr(fullClassName, loc, |
| /*Implicit=*/true); |
| Expr *call = CallExpr::createImplicit(ctx, ref, { className }, |
| { ctx.Id_className }); |
| ctor->setBody(BraceStmt::create(ctx, SourceLoc(), |
| ASTNode(call), |
| SourceLoc(), |
| /*implicit=*/true)); |
| } |
| |
| static std::tuple<GenericEnvironment *, GenericParamList *, SubstitutionMap> |
| configureGenericDesignatedInitOverride(ASTContext &ctx, |
| ClassDecl *classDecl, |
| Type superclassTy, |
| ConstructorDecl *superclassCtor) { |
| auto *superclassDecl = superclassTy->getAnyNominal(); |
| |
| auto *moduleDecl = classDecl->getParentModule(); |
| auto subMap = superclassTy->getContextSubstitutionMap( |
| moduleDecl, superclassDecl); |
| |
| GenericEnvironment *genericEnv; |
| |
| // Inheriting initializers that have their own generic parameters |
| auto *genericParams = superclassCtor->getGenericParams(); |
| if (genericParams) { |
| SmallVector<GenericTypeParamDecl *, 4> newParams; |
| |
| // First, clone the superclass constructor's generic parameter list, |
| // but change the depth of the generic parameters to be one greater |
| // than the depth of the subclass. |
| unsigned depth = 0; |
| if (auto *genericSig = classDecl->getGenericSignature()) |
| depth = genericSig->getGenericParams().back()->getDepth() + 1; |
| |
| for (auto *param : genericParams->getParams()) { |
| auto *newParam = new (ctx) GenericTypeParamDecl(classDecl, |
| param->getName(), |
| SourceLoc(), |
| depth, |
| param->getIndex()); |
| newParams.push_back(newParam); |
| } |
| |
| // We don't have to clone the requirements, because they're not |
| // used for anything. |
| genericParams = GenericParamList::create(ctx, |
| SourceLoc(), |
| newParams, |
| SourceLoc(), |
| ArrayRef<RequirementRepr>(), |
| SourceLoc()); |
| |
| // Build a generic signature for the derived class initializer. |
| GenericSignatureBuilder builder(ctx); |
| builder.addGenericSignature(classDecl->getGenericSignature()); |
| |
| // Add the generic parameters. |
| for (auto *newParam : newParams) |
| builder.addGenericParameter(newParam); |
| |
| auto source = |
| GenericSignatureBuilder::FloatingRequirementSource::forAbstract(); |
| auto *superclassSig = superclassCtor->getGenericSignature(); |
| |
| unsigned superclassDepth = 0; |
| if (auto *genericSig = superclassDecl->getGenericSignature()) |
| superclassDepth = genericSig->getGenericParams().back()->getDepth() + 1; |
| |
| // We're going to be substituting the requirements of the base class |
| // initializer to form the requirements of the derived class initializer. |
| auto substFn = [&](SubstitutableType *type) -> Type { |
| auto *gp = cast<GenericTypeParamType>(type); |
| if (gp->getDepth() < superclassDepth) |
| return Type(gp).subst(subMap); |
| return CanGenericTypeParamType::get( |
| gp->getDepth() - superclassDepth + depth, |
| gp->getIndex(), |
| ctx); |
| }; |
| |
| auto lookupConformanceFn = |
| [&](CanType depTy, Type substTy, ProtocolDecl *proto) |
| -> Optional<ProtocolConformanceRef> { |
| if (auto conf = subMap.lookupConformance(depTy, proto)) |
| return conf; |
| |
| return ProtocolConformanceRef(proto); |
| }; |
| |
| for (auto reqt : superclassSig->getRequirements()) |
| if (auto substReqt = reqt.subst(substFn, lookupConformanceFn)) |
| builder.addRequirement(*substReqt, source, nullptr); |
| |
| // Now form the substitution map that will be used to remap parameter |
| // types. |
| subMap = SubstitutionMap::get(superclassSig, |
| substFn, lookupConformanceFn); |
| |
| auto *genericSig = std::move(builder).computeGenericSignature(SourceLoc()); |
| genericEnv = genericSig->createGenericEnvironment(); |
| } else { |
| genericEnv = classDecl->getGenericEnvironment(); |
| } |
| |
| return std::make_tuple(genericEnv, genericParams, subMap); |
| } |
| |
| static void |
| configureInheritedDesignatedInitAttributes(TypeChecker &tc, |
| ClassDecl *classDecl, |
| ConstructorDecl *ctor, |
| ConstructorDecl *superclassCtor) { |
| assert(ctor->getDeclContext() == classDecl); |
| auto &ctx = tc.Context; |
| |
| AccessLevel access = classDecl->getFormalAccess(); |
| access = std::max(access, AccessLevel::Internal); |
| access = std::min(access, superclassCtor->getFormalAccess()); |
| |
| ctor->setAccess(access); |
| |
| AccessScope superclassInliningAccessScope = |
| superclassCtor->getFormalAccessScope(/*useDC*/nullptr, |
| /*usableFromInlineAsPublic=*/true); |
| |
| if (superclassInliningAccessScope.isPublic()) { |
| if (superclassCtor->getAttrs().hasAttribute<InlinableAttr>()) { |
| // Inherit the @inlinable attribute. |
| auto *clonedAttr = new (ctx) InlinableAttr(/*implicit=*/true); |
| ctor->getAttrs().add(clonedAttr); |
| |
| } else if (access == AccessLevel::Internal && !superclassCtor->isDynamic()){ |
| // Inherit the @usableFromInline attribute. |
| auto *clonedAttr = new (ctx) UsableFromInlineAttr(/*implicit=*/true); |
| ctor->getAttrs().add(clonedAttr); |
| } |
| } |
| |
| // Inherit the @discardableResult attribute. |
| if (superclassCtor->getAttrs().hasAttribute<DiscardableResultAttr>()) { |
| auto *clonedAttr = new (ctx) DiscardableResultAttr(/*implicit=*/true); |
| ctor->getAttrs().add(clonedAttr); |
| } |
| |
| // If the superclass has its own availability, make sure the synthesized |
| // constructor is only as available as its superclass's constructor. |
| if (superclassCtor->getAttrs().hasAttribute<AvailableAttr>()) { |
| SmallVector<Decl *, 2> asAvailableAs; |
| |
| // We don't have to look at enclosing contexts of the superclass constructor, |
| // because designated initializers must always be defined in the superclass |
| // body, and we already enforce that a superclass is at least as available as |
| // a subclass. |
| asAvailableAs.push_back(superclassCtor); |
| Decl *parentDecl = classDecl; |
| while (parentDecl != nullptr) { |
| asAvailableAs.push_back(parentDecl); |
| parentDecl = parentDecl->getDeclContext()->getAsDecl(); |
| } |
| AvailabilityInference::applyInferredAvailableAttrs( |
| ctor, asAvailableAs, ctx); |
| } |
| |
| // Wire up the overrides. |
| ctor->setOverriddenDecl(superclassCtor); |
| |
| if (superclassCtor->isRequired()) |
| ctor->getAttrs().add(new (ctx) RequiredAttr(/*IsImplicit=*/false)); |
| else |
| ctor->getAttrs().add(new (ctx) OverrideAttr(/*IsImplicit=*/false)); |
| |
| // If the superclass constructor is @objc but the subclass constructor is |
| // not representable in Objective-C, add @nonobjc implicitly. |
| Optional<ForeignErrorConvention> errorConvention; |
| if (superclassCtor->isObjC() && |
| !isRepresentableInObjC(ctor, ObjCReason::MemberOfObjCSubclass, |
| errorConvention)) |
| ctor->getAttrs().add(new (ctx) NonObjCAttr(/*isImplicit=*/true)); |
| } |
| |
| static void synthesizeDesignatedInitOverride(AbstractFunctionDecl *fn, |
| void *context) { |
| auto *ctor = cast<ConstructorDecl>(fn); |
| auto &ctx = ctor->getASTContext(); |
| |
| auto *bodyParams = ctor->getParameters(); |
| auto *superclassCtor = (ConstructorDecl *) context; |
| |
| // Reference to super.init. |
| auto *selfDecl = ctor->getImplicitSelfDecl(); |
| Expr *superRef = new (ctx) SuperRefExpr(selfDecl, SourceLoc(), |
| /*Implicit=*/true); |
| Expr *ctorRef = new (ctx) UnresolvedDotExpr(superRef, SourceLoc(), |
| superclassCtor->getFullName(), |
| DeclNameLoc(), |
| /*Implicit=*/true); |
| |
| auto ctorArgs = buildArgumentForwardingExpr(bodyParams->getArray(), ctx); |
| |
| Expr *superCall = |
| CallExpr::create(ctx, ctorRef, ctorArgs, |
| superclassCtor->getFullName().getArgumentNames(), { }, |
| /*hasTrailingClosure=*/false, /*implicit=*/true); |
| if (superclassCtor->hasThrows()) { |
| superCall = new (ctx) TryExpr(SourceLoc(), superCall, Type(), |
| /*implicit=*/true); |
| } |
| ctor->setBody(BraceStmt::create(ctx, SourceLoc(), |
| ASTNode(superCall), |
| SourceLoc(), |
| /*implicit=*/true)); |
| } |
| |
| ConstructorDecl * |
| swift::createDesignatedInitOverride(TypeChecker &tc, |
| ClassDecl *classDecl, |
| ConstructorDecl *superclassCtor, |
| DesignatedInitKind kind) { |
| auto &ctx = tc.Context; |
| |
| // Lookup will sometimes give us initializers that are from the ancestors of |
| // our immediate superclass. So, from the superclass constructor, we look |
| // one level up to the enclosing type context which will either be a class |
| // or an extension. We can use the type declared in that context to check |
| // if it's our immediate superclass and give up if we didn't. |
| // |
| // FIXME: Remove this when lookup of initializers becomes restricted to our |
| // immediate superclass. |
| auto *superclassCtorDecl = |
| superclassCtor->getDeclContext()->getSelfNominalTypeDecl(); |
| Type superclassTy = classDecl->getSuperclass(); |
| NominalTypeDecl *superclassDecl = superclassTy->getAnyNominal(); |
| if (superclassCtorDecl != superclassDecl) { |
| return nullptr; |
| } |
| |
| GenericEnvironment *genericEnv; |
| GenericParamList *genericParams; |
| SubstitutionMap subMap; |
| |
| std::tie(genericEnv, genericParams, subMap) = |
| configureGenericDesignatedInitOverride(ctx, |
| classDecl, |
| superclassTy, |
| superclassCtor); |
| |
| // Determine the initializer parameters. |
| |
| // Create the initializer parameter patterns. |
| OptionSet<ParameterList::CloneFlags> options = ParameterList::Implicit; |
| options |= ParameterList::Inherited; |
| auto *bodyParams = superclassCtor->getParameters()->clone(ctx, options); |
| |
| // If the superclass is generic, we need to map the superclass constructor's |
| // parameter types into the generic context of our class. |
| // |
| // We might have to apply substitutions, if for example we have a declaration |
| // like 'class A : B<Int>'. |
| for (auto *decl : *bodyParams) { |
| auto paramTy = decl->getInterfaceType(); |
| auto substTy = paramTy.subst(subMap); |
| decl->setInterfaceType(substTy); |
| } |
| |
| // Create the initializer declaration, inheriting the name, |
| // failability, and throws from the superclass initializer. |
| auto ctor = |
| new (ctx) ConstructorDecl(superclassCtor->getFullName(), |
| classDecl->getBraces().Start, |
| superclassCtor->getFailability(), |
| /*FailabilityLoc=*/SourceLoc(), |
| /*Throws=*/superclassCtor->hasThrows(), |
| /*ThrowsLoc=*/SourceLoc(), |
| bodyParams, genericParams, classDecl); |
| |
| ctor->setImplicit(); |
| |
| // Set the interface type of the initializer. |
| ctor->setGenericEnvironment(genericEnv); |
| ctor->computeType(); |
| |
| if (ctor->getFailability() == OTK_ImplicitlyUnwrappedOptional) { |
| ctor->getAttrs().add( |
| new (ctx) ImplicitlyUnwrappedOptionalAttr(/*implicit=*/true)); |
| } |
| |
| ctor->setValidationToChecked(); |
| |
| configureInheritedDesignatedInitAttributes(tc, classDecl, ctor, |
| superclassCtor); |
| |
| if (kind == DesignatedInitKind::Stub) { |
| // Make this a stub implementation. |
| ctor->setBodySynthesizer(synthesizeStubBody); |
| |
| // Note that this is a stub implementation. |
| ctor->setStubImplementation(true); |
| |
| // Stub constructors don't appear in the vtable. |
| ctor->setNeedsNewVTableEntry(false); |
| return ctor; |
| } |
| |
| // Form the body of a chaining designated initializer. |
| assert(kind == DesignatedInitKind::Chaining); |
| ctor->setBodySynthesizer(synthesizeDesignatedInitOverride, superclassCtor); |
| |
| return ctor; |
| } |