| //===--- TypeCheckDecl.cpp - Type Checking for Declarations ---------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See http://swift.org/LICENSE.txt for license information |
| // See http://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 "swift/AST/ASTWalker.h" |
| #include "swift/AST/Attr.h" |
| #include "swift/AST/Availability.h" |
| #include "swift/AST/Expr.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/ADT/StringExtras.h" |
| using namespace swift; |
| |
| const bool IsImplicit = true; |
| |
| /// 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 VarDecl *getParamDeclAtIndex(FuncDecl *fn, unsigned index) { |
| TuplePatternElt singleParam; |
| Pattern *paramPattern = fn->getBodyParamPatterns().back(); |
| ArrayRef<TuplePatternElt> params; |
| if (auto paramTuple = dyn_cast<TuplePattern>(paramPattern)) { |
| params = paramTuple->getElements(); |
| } else { |
| singleParam = TuplePatternElt( |
| cast<ParenPattern>(paramPattern)->getSubPattern()); |
| params = singleParam; |
| } |
| |
| auto firstParamPattern = params[index].getPattern(); |
| return firstParamPattern->getSingleVar(); |
| } |
| |
| static VarDecl *getFirstParamDecl(FuncDecl *fn) { |
| return getParamDeclAtIndex(fn, 0); |
| }; |
| |
| /// \brief Build an implicit 'self' parameter for the specified DeclContext. |
| static Pattern *buildImplicitSelfParameter(SourceLoc Loc, DeclContext *DC) { |
| ASTContext &Ctx = DC->getASTContext(); |
| auto *SelfDecl = new (Ctx) ParamDecl(/*IsLet*/ true, Loc, Identifier(), |
| Loc, Ctx.Id_self, Type(), DC); |
| SelfDecl->setImplicit(); |
| Pattern *P = new (Ctx) NamedPattern(SelfDecl, /*Implicit=*/true); |
| return new (Ctx) TypedPattern(P, TypeLoc()); |
| } |
| |
| static TuplePatternElt buildArgumentPattern(SourceLoc loc, DeclContext *DC, |
| StringRef name, Type type, |
| bool isLet, |
| VarDecl **paramDecl, |
| ASTContext &Context) { |
| auto *param = new (Context) ParamDecl(isLet, SourceLoc(), Identifier(), |
| loc, Context.getIdentifier(name), |
| Type(), DC); |
| if (paramDecl) *paramDecl = param; |
| param->setImplicit(); |
| |
| Pattern *valuePattern |
| = new (Context) TypedPattern(new (Context) NamedPattern(param, true), |
| TypeLoc::withoutLoc(type)); |
| valuePattern->setImplicit(); |
| |
| return TuplePatternElt(valuePattern); |
| } |
| |
| static TuplePatternElt buildLetArgumentPattern(SourceLoc loc, DeclContext *DC, |
| StringRef name, Type type, |
| VarDecl **paramDecl, |
| ASTContext &ctx) { |
| return buildArgumentPattern(loc, DC, name, type, |
| /*isLet*/ true, paramDecl, ctx); |
| } |
| |
| static TuplePatternElt buildInOutArgumentPattern(SourceLoc loc, DeclContext *DC, |
| StringRef name, Type type, |
| VarDecl **paramDecl, |
| ASTContext &ctx) { |
| return buildArgumentPattern(loc, DC, name, InOutType::get(type), |
| /*isLet*/ false, paramDecl, ctx); |
| } |
| |
| static Type getTypeOfStorage(AbstractStorageDecl *storage, |
| TypeChecker &TC) { |
| if (auto var = dyn_cast<VarDecl>(storage)) { |
| return TC.getTypeOfRValue(var, /*want interface type*/ false); |
| } else { |
| // None of the transformations done by getTypeOfRValue are |
| // necessary for subscripts. |
| auto subscript = cast<SubscriptDecl>(storage); |
| return subscript->getElementType(); |
| } |
| } |
| |
| static TuplePatternElt |
| buildSetterValueArgumentPattern(AbstractStorageDecl *storage, |
| VarDecl **valueDecl, TypeChecker &TC) { |
| auto storageType = getTypeOfStorage(storage, TC); |
| return buildLetArgumentPattern(storage->getLoc(), |
| storage->getDeclContext(), |
| "value", storageType, valueDecl, TC.Context); |
| } |
| |
| /// Build a pattern which can forward the formal index parameters of a |
| /// declaration. |
| /// |
| /// \param prefix optional arguments to be prefixed onto the index |
| /// forwarding pattern |
| static Pattern *buildIndexForwardingPattern(AbstractStorageDecl *storage, |
| MutableArrayRef<TuplePatternElt> prefix, |
| TypeChecker &TC) { |
| auto subscript = dyn_cast<SubscriptDecl>(storage); |
| |
| // Fast path: if this isn't a subscript, and we have a first |
| // pattern, we can just use that. |
| if (!subscript) { |
| auto tuple = TuplePattern::createSimple(TC.Context, SourceLoc(), prefix, |
| SourceLoc()); |
| tuple->setImplicit(); |
| return tuple; |
| } |
| |
| // Otherwise, we need to build up a new TuplePattern. |
| SmallVector<TuplePatternElt, 4> elements; |
| |
| // Start with the fields from the first pattern, if there are any. |
| elements.append(prefix.begin(), prefix.end()); |
| |
| // Clone index patterns in a manner that allows them to be |
| // perfectly forwarded. |
| DeclContext *DC = storage->getDeclContext(); |
| auto addVarPatternFor = [&](Pattern *P, Identifier label = Identifier()) { |
| Pattern *vp = P->cloneForwardable(TC.Context, DC, Pattern::Implicit); |
| elements.push_back(TuplePatternElt(vp)); |
| elements.back().setLabel(label, SourceLoc()); |
| }; |
| |
| // This is the same breakdown the parser does. |
| auto indices = subscript->getIndices(); |
| if (auto pp = dyn_cast<ParenPattern>(indices)) { |
| addVarPatternFor(pp); |
| } else { |
| auto tp = cast<TuplePattern>(indices); |
| for (auto &element : tp->getElements()) { |
| addVarPatternFor(element.getPattern(), element.getLabel()); |
| } |
| } |
| |
| return TuplePattern::createSimple(TC.Context, SourceLoc(), elements, |
| SourceLoc()); |
| } |
| |
| static FuncDecl *createGetterPrototype(AbstractStorageDecl *storage, |
| TypeChecker &TC) { |
| SourceLoc loc = storage->getLoc(); |
| |
| // Create the parameter list for the getter. |
| SmallVector<Pattern *, 2> getterParams; |
| |
| // The implicit 'self' argument if in a type context. |
| if (storage->getDeclContext()->isTypeContext()) |
| getterParams.push_back( |
| buildImplicitSelfParameter(loc, storage->getDeclContext())); |
| |
| // Add an index-forwarding clause. |
| getterParams.push_back(buildIndexForwardingPattern(storage, {}, TC)); |
| |
| SourceLoc staticLoc; |
| if (auto var = dyn_cast<VarDecl>(storage)) { |
| if (var->isStatic()) |
| staticLoc = var->getLoc(); |
| } |
| |
| auto storageType = getTypeOfStorage(storage, TC); |
| |
| auto getter = FuncDecl::create( |
| TC.Context, staticLoc, StaticSpellingKind::None, loc, Identifier(), loc, |
| SourceLoc(), SourceLoc(), /*GenericParams=*/nullptr, Type(), getterParams, |
| TypeLoc::withoutLoc(storageType), storage->getDeclContext()); |
| getter->setImplicit(); |
| |
| if (storage->isGetterMutating()) |
| getter->setMutating(); |
| |
| // If the var is marked final, then so is the getter. |
| if (storage->isFinal()) |
| makeFinal(TC.Context, getter); |
| |
| if (storage->isStatic()) |
| getter->setStatic(); |
| |
| return getter; |
| } |
| |
| static FuncDecl *createSetterPrototype(AbstractStorageDecl *storage, |
| VarDecl *&valueDecl, |
| TypeChecker &TC) { |
| SourceLoc loc = storage->getLoc(); |
| |
| // Create the parameter list for the setter. |
| SmallVector<Pattern *, 2> params; |
| |
| // The implicit 'self' argument if in a type context. |
| if (storage->getDeclContext()->isTypeContext()) { |
| params.push_back( |
| buildImplicitSelfParameter(loc, storage->getDeclContext())); |
| } |
| |
| // Add a "(value : T, indices...)" pattern. |
| TuplePatternElt valuePattern = |
| buildSetterValueArgumentPattern(storage, &valueDecl, TC); |
| params.push_back(buildIndexForwardingPattern(storage, valuePattern, TC)); |
| |
| Type setterRetTy = TupleType::getEmpty(TC.Context); |
| FuncDecl *setter = FuncDecl::create( |
| TC.Context, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, loc, |
| Identifier(), loc, SourceLoc(), SourceLoc(), /*generic=*/nullptr, Type(), |
| params, TypeLoc::withoutLoc(setterRetTy), storage->getDeclContext()); |
| setter->setImplicit(); |
| |
| if (!storage->isSetterNonMutating()) |
| setter->setMutating(); |
| |
| // If the var is marked final, then so is the getter. |
| if (storage->isFinal()) |
| makeFinal(TC.Context, setter); |
| |
| if (storage->isStatic()) |
| setter->setStatic(); |
| |
| return setter; |
| } |
| |
| /// Returns the type of the self argument of a materializeForSet |
| /// callback. If we don't have a meaningful direct self type, just |
| /// use something meaningless and hope it doesn't matter. |
| static Type getSelfTypeForMaterializeForSetCallback(ASTContext &ctx, |
| DeclContext *DC, |
| bool isStatic) { |
| Type selfType = DC->getDeclaredTypeInContext(); |
| if (!selfType) { |
| // This restriction is theoretically liftable by writing the necessary |
| // contextual information into the callback storage. |
| assert(!DC->isGenericContext() && |
| "no enclosing type for generic materializeForSet; callback " |
| "will not be able to bind type arguments!"); |
| return TupleType::getEmpty(ctx); |
| } |
| |
| // If we're in a protocol, we want to actually use the Self type. |
| if (selfType->is<ProtocolType>()) { |
| selfType = DC->getProtocolSelf()->getArchetype(); |
| } |
| |
| // Use the metatype if this is a static member. |
| if (isStatic) { |
| return MetatypeType::get(selfType, ctx); |
| } else { |
| return selfType; |
| } |
| } |
| |
| // True if the storage is dynamic or imported from Objective-C. In these cases, |
| // we need to emit a static materializeForSet thunk that dynamically dispatches |
| // to 'get' and 'set', rather than the normal dynamically dispatched |
| // materializeForSet that peer dispatches to 'get' and 'set'. |
| static bool needsDynamicMaterializeForSet(AbstractStorageDecl *storage) { |
| return storage->isDynamic() || storage->hasClangNode(); |
| } |
| |
| // True if a generated accessor needs to be registered as an external decl. |
| bool needsToBeRegisteredAsExternalDecl(AbstractStorageDecl *storage) { |
| // Either the storage itself was imported from Clang... |
| if (storage->hasClangNode()) |
| return true; |
| |
| // ...or it was synthesized into an imported type. |
| auto nominal = dyn_cast<NominalTypeDecl>(storage->getDeclContext()); |
| if (!nominal) |
| return false; |
| return nominal->hasClangNode(); |
| } |
| |
| static Type createMaterializeForSetReturnType(AbstractStorageDecl *storage, |
| TypeChecker &TC) { |
| auto &ctx = storage->getASTContext(); |
| SourceLoc loc = storage->getLoc(); |
| |
| auto DC = storage->getDeclContext(); |
| |
| if (DC->getDeclaredTypeInContext() && |
| DC->getDeclaredTypeInContext()->is<ErrorType>()) { |
| return ErrorType::get(ctx); |
| } |
| |
| Type callbackSelfType = |
| getSelfTypeForMaterializeForSetCallback(ctx, DC, storage->isStatic()); |
| TupleTypeElt callbackArgs[] = { |
| ctx.TheRawPointerType, |
| InOutType::get(ctx.TheUnsafeValueBufferType), |
| InOutType::get(callbackSelfType), |
| MetatypeType::get(callbackSelfType, MetatypeRepresentation::Thick), |
| }; |
| auto callbackExtInfo = FunctionType::ExtInfo() |
| .withRepresentation(FunctionType::Representation::Thin); |
| auto callbackType = FunctionType::get(TupleType::get(callbackArgs, ctx), |
| TupleType::getEmpty(ctx), |
| callbackExtInfo); |
| |
| // Try to make the callback type optional. Don't crash if it doesn't |
| // work, though. |
| auto optCallbackType = TC.getOptionalType(loc, callbackType); |
| if (!optCallbackType) optCallbackType = callbackType; |
| |
| TupleTypeElt retElts[] = { |
| { ctx.TheRawPointerType }, |
| { optCallbackType }, |
| }; |
| return TupleType::get(retElts, ctx); |
| } |
| |
| static FuncDecl *createMaterializeForSetPrototype(AbstractStorageDecl *storage, |
| VarDecl *&bufferParamDecl, |
| TypeChecker &TC) { |
| auto &ctx = storage->getASTContext(); |
| SourceLoc loc = storage->getLoc(); |
| |
| // Create the parameter list: |
| SmallVector<Pattern *, 2> params; |
| |
| // - The implicit 'self' argument if in a type context. |
| auto DC = storage->getDeclContext(); |
| if (DC->isTypeContext()) |
| params.push_back(buildImplicitSelfParameter(loc, DC)); |
| |
| // - The buffer parameter, (buffer: Builtin.RawPointer, |
| // inout storage: Builtin.UnsafeValueBuffer, |
| // indices...). |
| TuplePatternElt bufferElements[] = { |
| buildLetArgumentPattern(loc, DC, "buffer", ctx.TheRawPointerType, |
| &bufferParamDecl, TC.Context), |
| buildInOutArgumentPattern(loc, DC, "callbackStorage", |
| ctx.TheUnsafeValueBufferType, |
| nullptr, TC.Context), |
| }; |
| params.push_back(buildIndexForwardingPattern(storage, bufferElements, TC)); |
| |
| // The accessor returns (Builtin.RawPointer, (@convention(thin) (...) -> ())?), |
| // where the first pointer is the materialized address and the |
| // second is an optional callback. |
| Type retTy = createMaterializeForSetReturnType(storage, TC); |
| |
| auto *materializeForSet = FuncDecl::create( |
| ctx, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, loc, |
| Identifier(), loc, SourceLoc(), SourceLoc(), /*generic=*/nullptr, Type(), |
| params, TypeLoc::withoutLoc(retTy), DC); |
| materializeForSet->setImplicit(); |
| |
| // materializeForSet is mutating and static if the setter is. |
| auto setter = storage->getSetter(); |
| materializeForSet->setMutating(setter->isMutating()); |
| materializeForSet->setStatic(setter->isStatic()); |
| |
| // materializeForSet is final if the storage is. |
| if (storage->isFinal()) |
| makeFinal(ctx, materializeForSet); |
| |
| // If the storage is dynamic or ObjC-native, we can't add a dynamically- |
| // dispatched method entry for materializeForSet, so force it to be |
| // statically dispatched. ("final" would be inappropriate because the |
| // property can still be overridden.) |
| if (needsDynamicMaterializeForSet(storage)) |
| materializeForSet->setForcedStaticDispatch(true); |
| |
| // Make sure materializeForSet 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 (FuncDecl *setter = storage->getSetter()) { |
| asAvailableAs.push_back(setter); |
| } |
| |
| AvailabilityInference::applyInferredAvailableAttrs(materializeForSet, |
| asAvailableAs, ctx); |
| |
| // If the property came from ObjC, we need to register this as an external |
| // definition to be compiled. |
| if (needsToBeRegisteredAsExternalDecl(storage)) |
| TC.Context.addedExternalDecl(materializeForSet); |
| |
| return materializeForSet; |
| } |
| |
| void swift::convertStoredVarInProtocolToComputed(VarDecl *VD, TypeChecker &TC) { |
| auto *Get = createGetterPrototype(VD, TC); |
| |
| // Okay, we have both the getter and setter. Set them in VD. |
| VD->makeComputed(VD->getLoc(), Get, nullptr, nullptr, VD->getLoc()); |
| |
| // We've added some members to our containing class, add them to the members |
| // list. |
| addMemberToContextIfNeeded(Get, VD->getDeclContext()); |
| |
| // Type check the getter declaration. |
| TC.typeCheckDecl(VD->getGetter(), true); |
| TC.typeCheckDecl(VD->getGetter(), false); |
| } |
| |
| /// Build a tuple around the given arguments. |
| static Expr *buildTupleExpr(ASTContext &ctx, ArrayRef<Expr*> args) { |
| if (args.size() == 1) { |
| return args[0]; |
| } |
| SmallVector<Identifier, 4> labels(args.size()); |
| SmallVector<SourceLoc, 4> labelLocs(args.size()); |
| return TupleExpr::create(ctx, SourceLoc(), args, labels, labelLocs, |
| SourceLoc(), false, IsImplicit); |
| } |
| |
| |
| static Expr *buildTupleForwardingRefExpr(ASTContext &ctx, |
| ArrayRef<TuplePatternElt> params, |
| ArrayRef<TupleTypeElt> formalIndexTypes) { |
| assert(params.size() == formalIndexTypes.size()); |
| |
| SmallVector<Identifier, 4> labels; |
| SmallVector<SourceLoc, 4> labelLocs; |
| SmallVector<Expr *, 4> args; |
| |
| for (unsigned i = 0, e = params.size(); i != e; ++i) { |
| const Pattern *param = params[i].getPattern(); |
| args.push_back(param->buildForwardingRefExpr(ctx)); |
| labels.push_back(formalIndexTypes[i].getName()); |
| labelLocs.push_back(SourceLoc()); |
| } |
| |
| // A single unlabelled value is not a tuple. |
| if (args.size() == 1 && labels[0].empty()) |
| return args[0]; |
| |
| 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, FuncDecl *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. |
| TuplePatternElt singleParam; |
| Pattern *paramPattern = accessor->getBodyParamPatterns().back(); |
| ArrayRef<TuplePatternElt> params; |
| if (auto paramTuple = dyn_cast<TuplePattern>(paramPattern)) { |
| params = paramTuple->getElements(); |
| } else { |
| singleParam = TuplePatternElt( |
| cast<ParenPattern>(paramPattern)->getSubPattern()); |
| params = singleParam; |
| } |
| auto accessorKind = accessor->getAccessorKind(); |
| |
| // Ignore the value/buffer parameter. |
| if (accessorKind != AccessorKind::IsGetter) |
| params = params.slice(1); |
| |
| // Ignore the materializeForSet callback storage parameter. |
| if (accessorKind == AccessorKind::IsMaterializeForSet) |
| params = params.slice(1); |
| |
| // Look for formal subscript labels. |
| auto subscript = cast<SubscriptDecl>(accessor->getAccessorStorageDecl()); |
| auto indexType = subscript->getIndicesType(); |
| if (auto indexTuple = indexType->getAs<TupleType>()) { |
| return buildTupleForwardingRefExpr(ctx, params, indexTuple->getElements()); |
| } else { |
| return buildTupleForwardingRefExpr(ctx, params, TupleTypeElt(indexType)); |
| } |
| } |
| |
| enum class SelfAccessKind { |
| /// 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, |
| SelfAccessKind selfAccessKind, |
| TypeChecker &TC) { |
| switch (selfAccessKind) { |
| case SelfAccessKind::Peer: |
| return new (TC.Context) DeclRefExpr(selfDecl, SourceLoc(), IsImplicit); |
| |
| case SelfAccessKind::Super: |
| return new (TC.Context) SuperRefExpr(selfDecl, SourceLoc(), IsImplicit); |
| } |
| llvm_unreachable("bad self access kind"); |
| } |
| |
| namespace { |
| /// A simple helper interface for buildStorageReference. |
| class StorageReferenceContext { |
| StorageReferenceContext(const StorageReferenceContext &) = delete; |
| public: |
| StorageReferenceContext() = default; |
| virtual ~StorageReferenceContext() = default; |
| |
| /// Returns the declaration of the entity to use as the base of |
| /// the access, or nil if no base is required. |
| virtual VarDecl *getSelfDecl() const = 0; |
| |
| /// Returns an expression producing the index value, assuming that |
| /// the storage is a subscript declaration. |
| virtual Expr *getIndexRefExpr(ASTContext &ctx, |
| SubscriptDecl *subscript) const = 0; |
| }; |
| |
| /// A reference to storage from within an accessor. |
| class AccessorStorageReferenceContext : public StorageReferenceContext { |
| FuncDecl *Accessor; |
| public: |
| AccessorStorageReferenceContext(FuncDecl *accessor) : Accessor(accessor) {} |
| virtual ~AccessorStorageReferenceContext() = default; |
| |
| VarDecl *getSelfDecl() const override { |
| return Accessor->getImplicitSelfDecl(); |
| } |
| Expr *getIndexRefExpr(ASTContext &ctx, |
| SubscriptDecl *subscript) const override { |
| return buildSubscriptIndexReference(ctx, Accessor); |
| } |
| }; |
| } |
| |
| /// Build an l-value for the storage of a declaration. |
| static Expr *buildStorageReference( |
| const StorageReferenceContext &referenceContext, |
| AbstractStorageDecl *storage, |
| AccessSemantics semantics, |
| SelfAccessKind selfAccessKind, |
| TypeChecker &TC) { |
| ASTContext &ctx = TC.Context; |
| |
| VarDecl *selfDecl = referenceContext.getSelfDecl(); |
| if (!selfDecl) { |
| return new (ctx) DeclRefExpr(storage, SourceLoc(), IsImplicit, semantics); |
| } |
| |
| // If we should use a super access if applicable, and we have an |
| // overridden decl, then use ordinary access to it. |
| if (selfAccessKind == SelfAccessKind::Super) { |
| if (auto overridden = storage->getOverriddenDecl()) { |
| storage = overridden; |
| semantics = AccessSemantics::Ordinary; |
| } else { |
| selfAccessKind = SelfAccessKind::Peer; |
| } |
| } |
| |
| Expr *selfDRE = buildSelfReference(selfDecl, selfAccessKind, TC); |
| |
| if (auto subscript = dyn_cast<SubscriptDecl>(storage)) { |
| Expr *indices = referenceContext.getIndexRefExpr(ctx, subscript); |
| return new (ctx) SubscriptExpr(selfDRE, indices, storage, |
| IsImplicit, semantics); |
| } |
| |
| // This is a potentially polymorphic access, which is unnecessary; |
| // however, it shouldn't be problematic because any overrides |
| // should also redefine materializeForSet. |
| return new (ctx) MemberRefExpr(selfDRE, SourceLoc(), storage, |
| SourceLoc(), IsImplicit, semantics); |
| } |
| |
| static Expr *buildStorageReference(FuncDecl *accessor, |
| AbstractStorageDecl *storage, |
| AccessSemantics semantics, |
| SelfAccessKind selfAccessKind, |
| TypeChecker &TC) { |
| return buildStorageReference(AccessorStorageReferenceContext(accessor), |
| storage, semantics, selfAccessKind, TC); |
| } |
| |
| /// 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(FuncDecl *accessor, |
| AbstractStorageDecl *storage, |
| TypeChecker &TC) { |
| return buildStorageReference(accessor, storage, |
| AccessSemantics::DirectToStorage, |
| SelfAccessKind::Super, TC); |
| } |
| |
| /// Look up the NSCopying protocol from the Foundation module, if present. |
| /// Otherwise return null. |
| static ProtocolDecl *getNSCopyingProtocol(TypeChecker &TC, |
| DeclContext *DC) { |
| ASTContext &ctx = TC.Context; |
| auto foundation = ctx.getLoadedModule(ctx.Id_Foundation); |
| if (!foundation) |
| return nullptr; |
| |
| SmallVector<ValueDecl *, 2> results; |
| DC->lookupQualified(ModuleType::get(foundation), |
| ctx.getIdentifier("NSCopying"), |
| NL_QualifiedDefault | NL_KnownNonCascadingDependency, |
| /*resolver=*/nullptr, |
| results); |
| |
| if (results.size() != 1) |
| return nullptr; |
| |
| return dyn_cast<ProtocolDecl>(results.front()); |
| } |
| |
| |
| |
| /// 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.copyWithZone(nil)" |
| /// here. This does some type checking to validate that the call will succeed. |
| static Expr *synthesizeCopyWithZoneCall(Expr *Val, VarDecl *VD, |
| TypeChecker &TC) { |
| auto &Ctx = TC.Context; |
| |
| // We support @NSCopying on class types (which conform to NSCopying), |
| // protocols which conform, and option types thereof. |
| Type UnderlyingType = TC.getTypeOfRValue(VD, /*want interface type*/false); |
| |
| bool isOptional = false; |
| if (Type optionalEltTy = UnderlyingType->getAnyOptionalObjectType()) { |
| UnderlyingType = optionalEltTy; |
| isOptional = true; |
| } |
| |
| // The element type must conform to NSCopying. If not, emit an error and just |
| // recovery by synthesizing without the copy call. |
| auto *CopyingProto = getNSCopyingProtocol(TC, VD->getDeclContext()); |
| if (!CopyingProto || !TC.conformsToProtocol(UnderlyingType, CopyingProto, |
| VD->getDeclContext(), None)) { |
| TC.diagnose(VD->getLoc(), diag::nscopying_doesnt_conform); |
| 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 'copyWithZone' |
| // "Val") |
| // (paren_expr type='<null>' |
| // (nil_literal_expr type='<null>')))) |
| auto UDE = new (Ctx) UnresolvedDotExpr(Val, SourceLoc(), |
| Ctx.getIdentifier("copyWithZone"), |
| SourceLoc(), /*implicit*/true); |
| Expr *Nil = new (Ctx) NilLiteralExpr(SourceLoc(), /*implicit*/true); |
| Nil = new (Ctx) ParenExpr(SourceLoc(), Nil, SourceLoc(), false); |
| |
| //- (id)copyWithZone:(NSZone *)zone; |
| Expr *Call = new (Ctx) CallExpr(UDE, Nil, /*implicit*/true); |
| |
| TypeLoc ResultTy; |
| ResultTy.setType(VD->getType(), true); |
| |
| // 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(FuncDecl *accessor, |
| Expr *value, |
| AbstractStorageDecl *storage, |
| SmallVectorImpl<ASTNode> &body, |
| TypeChecker &TC) { |
| // 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, TC); |
| } |
| |
| // 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, |
| AccessSemantics::DirectToStorage, |
| SelfAccessKind::Super, TC); |
| |
| body.push_back(new (TC.Context) AssignExpr(dest, SourceLoc(), value, |
| IsImplicit)); |
| } |
| |
| /// 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). |
| static void maybeMarkTransparent(FuncDecl *accessor, |
| AbstractStorageDecl *storage, |
| TypeChecker &TC) { |
| auto *NTD = storage->getDeclContext() |
| ->isNominalTypeOrNominalTypeExtensionContext(); |
| |
| // FIXME: resilient global variables |
| if (!NTD || NTD->hasFixedLayout()) |
| accessor->getAttrs().add(new (TC.Context) TransparentAttr(IsImplicit)); |
| } |
| |
| /// 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 a direct |
| /// storage load. For an override of a base member property, it chains up to |
| /// super. |
| static void synthesizeTrivialGetter(FuncDecl *getter, |
| AbstractStorageDecl *storage, |
| TypeChecker &TC) { |
| auto &ctx = TC.Context; |
| |
| Expr *result = createPropertyLoadOrCallSuperclassGetter(getter, storage, TC); |
| ASTNode returnStmt = new (ctx) ReturnStmt(SourceLoc(), result, IsImplicit); |
| |
| SourceLoc loc = storage->getLoc(); |
| getter->setBody(BraceStmt::create(ctx, loc, returnStmt, loc, true)); |
| |
| maybeMarkTransparent(getter, storage, TC); |
| |
| // Register the accessor as an external decl if the storage was imported. |
| if (needsToBeRegisteredAsExternalDecl(storage)) |
| TC.Context.addedExternalDecl(getter); |
| } |
| |
| /// Synthesize the body of a trivial setter. |
| static void synthesizeTrivialSetter(FuncDecl *setter, |
| AbstractStorageDecl *storage, |
| VarDecl *valueVar, |
| TypeChecker &TC) { |
| if (storage->isInvalid()) return; |
| |
| auto &ctx = TC.Context; |
| SourceLoc loc = storage->getLoc(); |
| |
| auto *valueDRE = new (ctx) DeclRefExpr(valueVar, SourceLoc(), IsImplicit); |
| SmallVector<ASTNode, 1> setterBody; |
| createPropertyStoreOrCallSuperclassSetter(setter, valueDRE, storage, |
| setterBody, TC); |
| setter->setBody(BraceStmt::create(ctx, loc, setterBody, loc, true)); |
| |
| maybeMarkTransparent(setter, storage, TC); |
| |
| // Register the accessor as an external decl if the storage was imported. |
| if (needsToBeRegisteredAsExternalDecl(storage)) |
| TC.Context.addedExternalDecl(setter); |
| } |
| |
| /// Build the result expression of a materializeForSet accessor. |
| /// |
| /// \param address an expression yielding the address to return |
| /// \param callbackFn an optional closure expression for the callback |
| static Expr *buildMaterializeForSetResult(ASTContext &ctx, Expr *address, |
| Expr *callbackFn) { |
| if (!callbackFn) { |
| callbackFn = new (ctx) NilLiteralExpr(SourceLoc(), IsImplicit); |
| } |
| |
| return TupleExpr::create(ctx, SourceLoc(), { address, callbackFn }, |
| { Identifier(), Identifier() }, |
| { SourceLoc(), SourceLoc() }, |
| SourceLoc(), false, IsImplicit); |
| } |
| |
| /// Create a call to the builtin function with the given name. |
| static Expr *buildCallToBuiltin(ASTContext &ctx, StringRef builtinName, |
| ArrayRef<Expr*> args) { |
| auto builtin = getBuiltinValueDecl(ctx, ctx.getIdentifier(builtinName)); |
| Expr *builtinDRE = new (ctx) DeclRefExpr(builtin, SourceLoc(), IsImplicit); |
| Expr *arg = buildTupleExpr(ctx, args); |
| return new (ctx) CallExpr(builtinDRE, arg, IsImplicit); |
| } |
| |
| /// Synthesize the body of a materializeForSet accessor for a stored |
| /// property. |
| static void synthesizeStoredMaterializeForSet(FuncDecl *materializeForSet, |
| AbstractStorageDecl *storage, |
| VarDecl *bufferDecl, |
| TypeChecker &TC) { |
| ASTContext &ctx = TC.Context; |
| |
| // return (Builtin.addressof(&self.property), nil) |
| Expr *result = buildStorageReference(materializeForSet, storage, |
| AccessSemantics::DirectToStorage, |
| SelfAccessKind::Peer, TC); |
| result = new (ctx) InOutExpr(SourceLoc(), result, Type(), IsImplicit); |
| result = buildCallToBuiltin(ctx, "addressof", result); |
| result = buildMaterializeForSetResult(ctx, result, /*callback*/ nullptr); |
| |
| ASTNode returnStmt = new (ctx) ReturnStmt(SourceLoc(), result, IsImplicit); |
| |
| SourceLoc loc = storage->getLoc(); |
| materializeForSet->setBody(BraceStmt::create(ctx, loc, returnStmt, loc,true)); |
| |
| maybeMarkTransparent(materializeForSet, storage, TC); |
| |
| TC.typeCheckDecl(materializeForSet, true); |
| |
| // Register the accessor as an external decl if the storage was imported. |
| if (needsToBeRegisteredAsExternalDecl(storage)) |
| TC.Context.addedExternalDecl(materializeForSet); |
| } |
| |
| /// Does a storage decl currently lacking accessor functions require a |
| /// setter to be synthesized? |
| static bool doesStorageNeedSetter(AbstractStorageDecl *storage) { |
| assert(!storage->hasAccessorFunctions()); |
| switch (storage->getStorageKind()) { |
| // Add a setter to a stored variable unless it's a let. |
| case AbstractStorageDecl::Stored: |
| return !cast<VarDecl>(storage)->isLet(); |
| |
| // Addressed storage gets a setter if it has a mutable addressor. |
| case AbstractStorageDecl::Addressed: |
| return storage->getMutableAddressor() != nullptr; |
| |
| // These should already have accessor functions. |
| case AbstractStorageDecl::StoredWithTrivialAccessors: |
| case AbstractStorageDecl::StoredWithObservers: |
| case AbstractStorageDecl::InheritedWithObservers: |
| case AbstractStorageDecl::AddressedWithTrivialAccessors: |
| case AbstractStorageDecl::AddressedWithObservers: |
| case AbstractStorageDecl::ComputedWithMutableAddress: |
| llvm_unreachable("already has accessor functions"); |
| |
| case AbstractStorageDecl::Computed: |
| llvm_unreachable("not stored"); |
| } |
| llvm_unreachable("bad storage kind"); |
| } |
| |
| /// Add a materializeForSet accessor to the given declaration. |
| static FuncDecl *addMaterializeForSet(AbstractStorageDecl *storage, |
| TypeChecker &TC) { |
| VarDecl *bufferDecl; |
| auto materializeForSet = |
| createMaterializeForSetPrototype(storage, bufferDecl, TC); |
| addMemberToContextIfNeeded(materializeForSet, storage->getDeclContext(), |
| storage->getSetter()); |
| storage->setMaterializeForSetFunc(materializeForSet); |
| |
| TC.computeAccessibility(materializeForSet); |
| |
| TC.validateDecl(materializeForSet); |
| |
| return materializeForSet; |
| } |
| |
| /// Add trivial accessors to a Stored or Addressed property. |
| void swift::addTrivialAccessorsToStorage(AbstractStorageDecl *storage, |
| TypeChecker &TC) { |
| assert(!storage->hasAccessorFunctions() && "already has accessors?"); |
| |
| // Create the getter. |
| auto *getter = createGetterPrototype(storage, TC); |
| |
| // Create the setter. |
| FuncDecl *setter = nullptr; |
| VarDecl *setterValueParam = nullptr; |
| if (doesStorageNeedSetter(storage)) { |
| setter = createSetterPrototype(storage, setterValueParam, TC); |
| } |
| |
| // Okay, we have both the getter and setter. Set them in VD. |
| storage->addTrivialAccessors(getter, setter, nullptr); |
| |
| bool isDynamic = (storage->isDynamic() && storage->isObjC()); |
| if (isDynamic) |
| getter->getAttrs().add(new (TC.Context) DynamicAttr(IsImplicit)); |
| |
| // Synthesize and type-check the body of the getter. |
| synthesizeTrivialGetter(getter, storage, TC); |
| TC.typeCheckDecl(getter, true); |
| TC.typeCheckDecl(getter, false); |
| |
| if (setter) { |
| if (isDynamic) |
| setter->getAttrs().add(new (TC.Context) DynamicAttr(IsImplicit)); |
| |
| // Synthesize and type-check the body of the setter. |
| synthesizeTrivialSetter(setter, storage, setterValueParam, TC); |
| TC.typeCheckDecl(setter, true); |
| TC.typeCheckDecl(setter, false); |
| } |
| |
| // We've added some members to our containing type, add them to the |
| // members list. |
| addMemberToContextIfNeeded(getter, storage->getDeclContext()); |
| if (setter) |
| addMemberToContextIfNeeded(setter, storage->getDeclContext()); |
| |
| // Always add a materializeForSet when we're creating trivial |
| // accessors for a mutable stored property. We only do this when we |
| // need to be able to access something polymorphically, and we always |
| // want a materializeForSet in such situations. |
| if (setter) { |
| FuncDecl *materializeForSet = addMaterializeForSet(storage, TC); |
| synthesizeMaterializeForSet(materializeForSet, storage, TC); |
| TC.typeCheckDecl(materializeForSet, true); |
| TC.typeCheckDecl(materializeForSet, false); |
| } |
| } |
| |
| /// Add a trivial setter and materializeForSet to a |
| /// ComputedWithMutableAddress storage decl. |
| void swift:: |
| synthesizeSetterForMutableAddressedStorage(AbstractStorageDecl *storage, |
| TypeChecker &TC) { |
| auto setter = storage->getSetter(); |
| assert(setter); |
| assert(!storage->getSetter()->getBody()); |
| assert(storage->getStorageKind() == |
| AbstractStorageDecl::ComputedWithMutableAddress); |
| |
| // Synthesize and type-check the body of the setter. |
| VarDecl *valueParamDecl = getFirstParamDecl(setter); |
| synthesizeTrivialSetter(setter, storage, valueParamDecl, TC); |
| TC.typeCheckDecl(setter, true); |
| TC.typeCheckDecl(setter, false); |
| } |
| |
| /// 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) { |
| // If the decl is stored, convert it to StoredWithTrivialAccessors |
| // by synthesizing the full set of accessors. |
| if (!storage->hasAccessorFunctions()) { |
| addTrivialAccessorsToStorage(storage, *this); |
| return; |
| } |
| |
| // Otherwise, if the requirement is settable, ensure that there's a |
| // materializeForSet function. |
| // |
| // @objc protocols don't need a materializeForSet since ObjC doesn't have |
| // that concept. |
| if (!requirement->isObjC() && |
| requirement->getSetter() && !storage->getMaterializeForSetFunc()) { |
| FuncDecl *materializeForSet = addMaterializeForSet(storage, *this); |
| synthesizeMaterializeForSet(materializeForSet, storage, *this); |
| typeCheckDecl(materializeForSet, true); |
| typeCheckDecl(materializeForSet, false); |
| } |
| return; |
| } |
| |
| using CallbackGenerator = |
| llvm::function_ref<void(SmallVectorImpl<ASTNode> &callbackBody, |
| VarDecl *selfDecl, VarDecl *bufferDecl, |
| VarDecl *callbackStorageDecl)>; |
| |
| /// Build a materializeForSet callback function. |
| /// It should have type |
| /// (Builtin.RawPointer, inout Builtin.UnsafeValueBuffer, |
| /// inout T, T.Type) -> () |
| static Expr *buildMaterializeForSetCallback(ASTContext &ctx, |
| FuncDecl *materializeForSet, |
| AbstractStorageDecl *storage, |
| const CallbackGenerator &generator) { |
| |
| auto DC = storage->getDeclContext(); |
| SourceLoc loc = storage->getLoc(); |
| |
| Type selfType = |
| getSelfTypeForMaterializeForSetCallback(ctx, DC, |
| materializeForSet->isStatic()); |
| |
| // Build the parameters pattern. |
| // |
| // Unexpected subtlety: it actually important to call the inout self |
| // parameter something other than 'self' so that we don't trigger |
| // the "implicit use of self" diagnostic. |
| VarDecl *bufferDecl; |
| VarDecl *callbackStorageDecl; |
| VarDecl *selfDecl; |
| TuplePatternElt argPatterns[] = { |
| buildLetArgumentPattern(loc, DC, "buffer", ctx.TheRawPointerType, |
| &bufferDecl, ctx), |
| buildInOutArgumentPattern(loc, DC, "callbackStorage", |
| ctx.TheUnsafeValueBufferType, |
| &callbackStorageDecl, ctx), |
| buildInOutArgumentPattern(loc, DC, "selfValue", selfType, &selfDecl, ctx), |
| buildLetArgumentPattern(loc, DC, "selfType", MetatypeType::get(selfType), |
| nullptr, ctx), |
| }; |
| auto args = TuplePattern::createSimple(ctx, SourceLoc(), argPatterns, |
| SourceLoc()); |
| args->setImplicit(); |
| |
| // Create the closure expression itself. |
| auto closure = new (ctx) ClosureExpr(args, SourceLoc(), SourceLoc(), |
| SourceLoc(), TypeLoc(), |
| /*discriminator*/ 0, materializeForSet); |
| |
| // Generate the body of the closure. |
| SmallVector<ASTNode, 4> body; |
| generator(body, selfDecl, bufferDecl, callbackStorageDecl); |
| closure->setBody(BraceStmt::create(ctx, SourceLoc(), body, SourceLoc(), |
| IsImplicit), |
| /*isSingleExpression*/ false); |
| closure->setImplicit(IsImplicit); |
| |
| // Call our special builtin to turn that into an opaque thin function. |
| auto result = buildCallToBuiltin(ctx, "makeMaterializeForSetCallback", |
| { closure }); |
| return result; |
| } |
| |
| /// Build a builtin operation on a Builtin.UnsafeValueBuffer. |
| static Expr *buildValueBufferOperation(ASTContext &ctx, StringRef builtinName, |
| VarDecl *bufferDecl, Type valueType) { |
| // &buffer |
| Expr *bufferRef = new (ctx) DeclRefExpr(bufferDecl, SourceLoc(), IsImplicit); |
| bufferRef = new (ctx) InOutExpr(SourceLoc(), bufferRef, Type(), IsImplicit); |
| |
| // T.self |
| Expr *metatypeRef = TypeExpr::createImplicit(valueType, ctx); |
| metatypeRef = new (ctx) DotSelfExpr(metatypeRef, SourceLoc(), SourceLoc()); |
| metatypeRef->setImplicit(IsImplicit); |
| |
| // Builtin.whatever(&buffer, T.self) |
| return buildCallToBuiltin(ctx, builtinName, { bufferRef, metatypeRef }); |
| } |
| |
| /// Build a call to Builtin.take. |
| static Expr *buildBuiltinTake(ASTContext &ctx, Expr *address, |
| Type valueType) { |
| // Builtin.take(address) as ValueType |
| Expr *result = buildCallToBuiltin(ctx, "take", { address }); |
| result = new (ctx) CoerceExpr(result, SourceLoc(), |
| TypeLoc::withoutLoc(valueType)); |
| result->setImplicit(IsImplicit); |
| return result; |
| } |
| |
| /// Build an expression to initialize callback storage. |
| static ASTNode buildInitializeCallbackStorage(FuncDecl *materializeForSet, |
| Expr *initializer, |
| Type initializerType, |
| ASTContext &ctx) { |
| // let allocatedCallbackStorage = |
| // Builtin.allocateValueBuffer(&callbackStorage, IndexType.self)) |
| VarDecl *callbackStorageDecl = getParamDeclAtIndex(materializeForSet, 1); |
| Expr *allocatedCallbackStorage = |
| buildValueBufferOperation(ctx, "allocValueBuffer", callbackStorageDecl, |
| initializerType); |
| |
| // Builtin.initialize(indexArgs, allocatedCallbackStorage) |
| return buildCallToBuiltin(ctx, "initialize", |
| { initializer, allocatedCallbackStorage }); |
| } |
| |
| /// Build an expression to take from callback storage. |
| static Expr *buildTakeFromCallbackStorage(VarDecl *storage, Type valueType, |
| ASTContext &ctx) { |
| Expr *address = |
| buildValueBufferOperation(ctx, "projectValueBuffer", storage, valueType); |
| return buildBuiltinTake(ctx, address, valueType); |
| } |
| |
| namespace { |
| /// A reference to storage from the context of a materializeForSet |
| /// callback. |
| class CallbackStorageReferenceContext : public StorageReferenceContext { |
| VarDecl *Self; |
| VarDecl *CallbackStorage; |
| public: |
| CallbackStorageReferenceContext(VarDecl *self, VarDecl *callbackStorage) |
| : Self(self), CallbackStorage(callbackStorage) {} |
| virtual ~CallbackStorageReferenceContext() = default; |
| |
| VarDecl *getSelfDecl() const override { |
| return Self; |
| } |
| Expr *getIndexRefExpr(ASTContext &ctx, |
| SubscriptDecl *subscript) const override { |
| return buildTakeFromCallbackStorage(CallbackStorage, |
| subscript->getIndicesType(), ctx); |
| } |
| }; |
| } |
| |
| /// Synthesize the body of a materializeForSet accessor for a |
| /// computed property. |
| static void synthesizeComputedMaterializeForSet(FuncDecl *materializeForSet, |
| AbstractStorageDecl *storage, |
| VarDecl *bufferDecl, |
| TypeChecker &TC) { |
| ASTContext &ctx = TC.Context; |
| |
| SmallVector<ASTNode, 4> body; |
| |
| AccessSemantics semantics; |
| // If the storage is dynamic, we must dynamically redispatch through the |
| // accessor. Otherwise, we can do a direct peer access. |
| if (needsDynamicMaterializeForSet(storage)) |
| semantics = AccessSemantics::Ordinary; |
| else |
| semantics = AccessSemantics::DirectToAccessor; |
| |
| // Builtin.initialize(self.property, buffer) |
| Expr *curValue = buildStorageReference(materializeForSet, storage, |
| semantics, |
| SelfAccessKind::Peer, TC); |
| Expr *bufferRef = new (ctx) DeclRefExpr(bufferDecl, SourceLoc(), IsImplicit); |
| body.push_back(buildCallToBuiltin(ctx, "initialize", |
| { curValue, bufferRef })); |
| |
| // If this is a subscript, preserve the index value: |
| if (auto subscript = dyn_cast<SubscriptDecl>(storage)) { |
| Expr *indices = buildSubscriptIndexReference(ctx, materializeForSet); |
| ASTNode initialize = |
| buildInitializeCallbackStorage(materializeForSet, indices, |
| subscript->getIndicesType(), ctx); |
| body.push_back(initialize); |
| } |
| |
| // Build the callback. |
| Expr *callback = |
| buildMaterializeForSetCallback(ctx, materializeForSet, storage, |
| [&](SmallVectorImpl<ASTNode> &body, |
| VarDecl *selfDecl, VarDecl *bufferDecl, |
| VarDecl *callbackStorageDecl) { |
| // self.property = Builtin.take(buffer) |
| Expr *bufferRef = |
| new (ctx) DeclRefExpr(bufferDecl, SourceLoc(), IsImplicit); |
| Expr *value = buildBuiltinTake(ctx, bufferRef, |
| getTypeOfStorage(storage, TC)); |
| |
| Expr *storageRef = |
| buildStorageReference( |
| CallbackStorageReferenceContext{selfDecl, callbackStorageDecl}, |
| storage, semantics, |
| SelfAccessKind::Peer, TC); |
| body.push_back(new (ctx) AssignExpr(storageRef, SourceLoc(), |
| value, IsImplicit)); |
| |
| // If this is a subscript, deallocate the subscript buffer: |
| if (auto subscript = dyn_cast<SubscriptDecl>(storage)) { |
| // Builtin.deallocValueBuffer(&callbackStorage, IndexType.self) |
| body.push_back(buildValueBufferOperation(ctx, "deallocValueBuffer", |
| callbackStorageDecl, |
| subscript->getIndicesType())); |
| } |
| }); |
| |
| // return (buffer, callback) |
| Expr *result = new (ctx) DeclRefExpr(bufferDecl, SourceLoc(), IsImplicit); |
| |
| result = buildMaterializeForSetResult(ctx, result, callback); |
| body.push_back(new (ctx) ReturnStmt(SourceLoc(), result, IsImplicit)); |
| |
| SourceLoc loc = storage->getLoc(); |
| materializeForSet->setBody(BraceStmt::create(ctx, loc, body, loc, true)); |
| |
| // Mark it transparent, there is no user benefit to this actually existing. |
| materializeForSet->getAttrs().add(new (ctx) TransparentAttr(IsImplicit)); |
| |
| TC.typeCheckDecl(materializeForSet, true); |
| |
| // Register the accessor as an external decl if the storage was imported. |
| if (needsToBeRegisteredAsExternalDecl(storage)) |
| TC.Context.addedExternalDecl(materializeForSet); |
| } |
| |
| /// Build a direct call to an addressor from within a |
| /// materializeForSet method. |
| static Expr *buildCallToAddressor(FuncDecl *materializeForSet, |
| AbstractStorageDecl *storage, |
| VarDecl *bufferDecl, |
| FuncDecl *addressor, |
| ASTContext &ctx) { |
| // Build a direct reference to the addressor. |
| Expr *fn; |
| |
| // Apply the self argument if applicable. |
| if (auto self = materializeForSet->getImplicitSelfDecl()) { |
| Expr *selfRef = new (ctx) DeclRefExpr(self, SourceLoc(), IsImplicit); |
| // if (addressor->computeSelfType(nullptr)->is<LValueType>()) { |
| // selfRef = new (ctx) InOutExpr(SourceLoc(), selfRef, Type(), IsImplicit); |
| // } |
| |
| ValueDecl *localMembers[] = { addressor }; |
| fn = new (ctx) OverloadedMemberRefExpr(selfRef, SourceLoc(), |
| ctx.AllocateCopy(localMembers), |
| SourceLoc(), IsImplicit, Type(), |
| AccessSemantics::DirectToStorage); |
| } else { |
| fn = new (ctx) DeclRefExpr(addressor, SourceLoc(), IsImplicit, |
| AccessSemantics::DirectToStorage); |
| } |
| |
| // Apply the rest of the addressor arguments. |
| Expr *args; |
| if (isa<SubscriptDecl>(storage)) { |
| args = buildSubscriptIndexReference(ctx, materializeForSet); |
| } else { |
| args = TupleExpr::createImplicit(ctx, {}, {}); |
| } |
| |
| return new (ctx) CallExpr(fn, args, IsImplicit); |
| } |
| |
| /// Given an expression of type UnsafeMutablePointer<T>, create an |
| /// expression of type Builtin.RawPointer. |
| static Expr *buildUnsafeMutablePointerToRawPointer(Expr *operand, |
| ASTContext &ctx) { |
| // Just directly drill in. |
| NominalTypeDecl *ptrType = ctx.getUnsafeMutablePointerDecl(); |
| |
| // If that doesn't work, just bail out; the result probably won't |
| // type-check, but there are worse failure modes for a broken stdlib. |
| if (!ptrType) return operand; |
| auto props = ptrType->getStoredProperties(); |
| if (props.begin() == props.end()) return operand; |
| |
| auto storageProp = props.front(); |
| return new (ctx) MemberRefExpr(operand, SourceLoc(), storageProp, |
| SourceRange(), IsImplicit); |
| } |
| |
| /// Synthesize the body of a materializeForSet accessor for an |
| /// addressed property. |
| static void synthesizeAddressedMaterializeForSet(FuncDecl *materializeForSet, |
| AbstractStorageDecl *storage, |
| VarDecl *bufferDecl, |
| TypeChecker &TC) { |
| ASTContext &ctx = TC.Context; |
| |
| SmallVector<ASTNode, 4> body; |
| |
| // Call the mutable addressor. |
| auto addressor = storage->getMutableAddressor(); |
| Expr *addressorResult = buildCallToAddressor(materializeForSet, storage, |
| bufferDecl, addressor, ctx); |
| |
| Expr *result; |
| Expr *callback; |
| switch (addressor->getAddressorKind()) { |
| case AddressorKind::NotAddressor: |
| llvm_unreachable("addressor is not an addressor?"); |
| |
| // If we have an unsafe addressor, this is easy: we just pull out |
| // the raw pointer and use that as the result. |
| case AddressorKind::Unsafe: |
| result = buildUnsafeMutablePointerToRawPointer(addressorResult, ctx); |
| callback = nullptr; |
| break; |
| |
| case AddressorKind::Owning: |
| case AddressorKind::NativeOwning: |
| case AddressorKind::NativePinning: { |
| // We need to bind the result to a temporary variable. |
| // let temporary = addressor(self)(indices) |
| auto tempDecl = new (ctx) VarDecl(/*static*/ false, /*let*/ true, |
| SourceLoc(), ctx.getIdentifier("tmp"), |
| Type(), materializeForSet); |
| tempDecl->setImplicit(IsImplicit); |
| auto bindingPattern = new (ctx) NamedPattern(tempDecl, IsImplicit); |
| auto bindingDecl = PatternBindingDecl::create(ctx, /*static*/ SourceLoc(), |
| StaticSpellingKind::None, |
| SourceLoc(), bindingPattern, |
| addressorResult, |
| materializeForSet); |
| bindingDecl->setImplicit(IsImplicit); |
| body.push_back(bindingDecl); |
| body.push_back(tempDecl); |
| |
| // This should be Builtin.NativePointer or something like it. |
| Type ownerType = [&]() -> Type { |
| switch (addressor->getAddressorKind()){ |
| case AddressorKind::NotAddressor: |
| case AddressorKind::Unsafe: |
| llvm_unreachable("filtered out"); |
| case AddressorKind::Owning: |
| return ctx.TheUnknownObjectType; |
| case AddressorKind::NativeOwning: |
| return ctx.TheNativeObjectType; |
| case AddressorKind::NativePinning: |
| return OptionalType::get(ctx.TheNativeObjectType); |
| } |
| llvm_unreachable("bad addressor kind"); |
| }(); |
| |
| // Initialize the callback storage with the owner value, which is |
| // the second element of the addressor result. |
| Expr *owner = new (ctx) DeclRefExpr(tempDecl, SourceLoc(), IsImplicit); |
| owner = new (ctx) TupleElementExpr(owner, SourceLoc(), /*field index*/ 1, |
| SourceLoc(), Type()); |
| owner->setImplicit(IsImplicit); |
| body.push_back(buildInitializeCallbackStorage(materializeForSet, owner, |
| ownerType, ctx)); |
| |
| // The result is the first element of the addressor result. |
| result = new (ctx) DeclRefExpr(tempDecl, SourceLoc(), IsImplicit); |
| result = new (ctx) TupleElementExpr(result, SourceLoc(), /*field index*/ 0, |
| SourceLoc(), Type()); |
| result->setImplicit(IsImplicit); |
| result = buildUnsafeMutablePointerToRawPointer(result, ctx); |
| |
| // Build the callback. |
| callback = buildMaterializeForSetCallback(ctx, materializeForSet, storage, |
| [&](SmallVectorImpl<ASTNode> &body, VarDecl *selfDecl, |
| VarDecl *bufferDecl, VarDecl *callbackStorageDecl) { |
| // Pull the owner out of callback storage. |
| Expr *owner = |
| buildTakeFromCallbackStorage(callbackStorageDecl, ownerType, ctx); |
| |
| // For an owning addressor, we can just drop the value we pulled out. |
| if (addressor->getAddressorKind() != AddressorKind::NativePinning) { |
| body.push_back(owner); |
| |
| // For a pinning addressor, we have to unpin it. |
| } else { |
| Expr *unpin = buildCallToBuiltin(ctx, "unpin", { owner }); |
| body.push_back(unpin); |
| } |
| |
| // This should always be a no-op, but do it for the sake of formalism. |
| // Builtin.deallocValueBuffer(&callbackStorage, OwnerType.self) |
| body.push_back(buildValueBufferOperation(ctx, "deallocValueBuffer", |
| callbackStorageDecl, |
| ownerType)); |
| }); |
| break; |
| } |
| } |
| |
| // return (buffer, callback) |
| result = buildMaterializeForSetResult(ctx, result, callback); |
| body.push_back(new (ctx) ReturnStmt(SourceLoc(), result, IsImplicit)); |
| |
| SourceLoc loc = storage->getLoc(); |
| materializeForSet->setBody(BraceStmt::create(ctx, loc, body, loc)); |
| |
| // Mark it transparent, there is no user benefit to this actually existing. |
| materializeForSet->getAttrs().add(new (ctx) TransparentAttr(IsImplicit)); |
| |
| TC.typeCheckDecl(materializeForSet, true); |
| |
| // Register the accessor as an external decl if the storage was imported. |
| if (needsToBeRegisteredAsExternalDecl(storage)) |
| TC.Context.addedExternalDecl(materializeForSet); |
| } |
| |
| void swift::synthesizeMaterializeForSet(FuncDecl *materializeForSet, |
| AbstractStorageDecl *storage, |
| TypeChecker &TC) { |
| VarDecl *bufferDecl = getFirstParamDecl(materializeForSet); |
| |
| switch (storage->getStorageKind()) { |
| case AbstractStorageDecl::Stored: |
| case AbstractStorageDecl::Addressed: |
| llvm_unreachable("no accessors"); |
| |
| // We can use direct access to stored variables, but not if they're |
| // weak, unowned, or dynamic. |
| case AbstractStorageDecl::StoredWithTrivialAccessors: { |
| // Only variables can be Stored, and only variables can be weak/unowned. |
| auto var = cast<VarDecl>(storage); |
| if (var->getType()->is<ReferenceStorageType>() |
| || needsDynamicMaterializeForSet(var)) { |
| synthesizeComputedMaterializeForSet(materializeForSet, storage, |
| bufferDecl, TC); |
| return; |
| } |
| |
| synthesizeStoredMaterializeForSet(materializeForSet, storage, |
| bufferDecl, TC); |
| return; |
| } |
| |
| // We should access these by calling mutableAddress. |
| case AbstractStorageDecl::AddressedWithTrivialAccessors: |
| case AbstractStorageDecl::ComputedWithMutableAddress: |
| synthesizeAddressedMaterializeForSet(materializeForSet, storage, |
| bufferDecl, TC); |
| return; |
| |
| // These must be accessed with a getter/setter pair. |
| // TODO: StoredWithObservers and AddressedWithObservers could be |
| // made to work with the callback as long as there isn't a willSet. |
| case AbstractStorageDecl::StoredWithObservers: |
| case AbstractStorageDecl::InheritedWithObservers: |
| case AbstractStorageDecl::AddressedWithObservers: |
| case AbstractStorageDecl::Computed: |
| synthesizeComputedMaterializeForSet(materializeForSet, storage, |
| bufferDecl, TC); |
| return; |
| } |
| llvm_unreachable("bad abstract storage kind"); |
| } |
| |
| /// Given a VarDecl with a willSet: and/or didSet: specifier, synthesize the |
| /// (trivial) getter and the setter, which calls these. |
| void swift::synthesizeObservingAccessors(VarDecl *VD, TypeChecker &TC) { |
| assert(VD->hasObservers()); |
| assert(VD->getGetter() && VD->getSetter() && |
| !VD->getGetter()->hasBody() && !VD->getSetter()->hasBody() && |
| "willSet/didSet var already has a getter or setter"); |
| |
| auto &Ctx = VD->getASTContext(); |
| SourceLoc Loc = VD->getLoc(); |
| |
| // The getter is always trivial: just perform a (direct!) load of storage, or |
| // a call of a superclass getter if this is an override. |
| auto *Get = VD->getGetter(); |
| synthesizeTrivialGetter(Get, VD, TC); |
| |
| // Okay, the getter is done, create the setter now. Start by finding the |
| // decls for 'self' and 'value'. |
| auto *Set = VD->getSetter(); |
| auto *SelfDecl = Set->getImplicitSelfDecl(); |
| VarDecl *ValueDecl = nullptr; |
| Set->getBodyParamPatterns().back()->forEachVariable([&](VarDecl *VD) { |
| assert(!ValueDecl && "Already found 'value'?"); |
| ValueDecl = VD; |
| }); |
| |
| // 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, TC); |
| |
| OldValue = new (Ctx) VarDecl(/*isStatic*/false, /*isLet*/ true, |
| SourceLoc(), Ctx.getIdentifier("tmp"), |
| Type(), Set); |
| OldValue->setImplicit(); |
| auto *tmpPattern = new (Ctx) NamedPattern(OldValue, /*implicit*/ true); |
| auto tmpPBD = PatternBindingDecl::create(Ctx, SourceLoc(), |
| StaticSpellingKind::None, |
| SourceLoc(), |
| tmpPattern, OldValueExpr, Set); |
| tmpPBD->setImplicit(); |
| 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, SourceLoc(), /*imp*/true); |
| auto *ValueDRE = new (Ctx) DeclRefExpr(ValueDecl, SourceLoc(), /*imp*/true); |
| if (SelfDecl) { |
| auto *SelfDRE = new (Ctx) DeclRefExpr(SelfDecl, SourceLoc(), /*imp*/true); |
| Callee = new (Ctx) DotSyntaxCallExpr(Callee, SourceLoc(), SelfDRE); |
| } |
| SetterBody.push_back(new (Ctx) CallExpr(Callee, ValueDRE, true)); |
| |
| // Make sure the didSet/willSet accessors are marked final if in a class. |
| if (!willSet->isFinal() && |
| VD->getDeclContext()->isClassOrClassExtensionContext()) |
| makeFinal(Ctx, willSet); |
| } |
| |
| // Create an assignment into the storage or call to superclass setter. |
| auto *ValueDRE = new (Ctx) DeclRefExpr(ValueDecl, SourceLoc(), true); |
| createPropertyStoreOrCallSuperclassSetter(Set, ValueDRE, VD, SetterBody, TC); |
| |
| // 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, SourceLoc(), |
| /*impl*/true); |
| Expr *Callee = new (Ctx) DeclRefExpr(didSet, SourceLoc(), /*imp*/true); |
| if (SelfDecl) { |
| auto *SelfDRE = new (Ctx) DeclRefExpr(SelfDecl, SourceLoc(), /*imp*/true); |
| Callee = new (Ctx) DotSyntaxCallExpr(Callee, SourceLoc(), SelfDRE); |
| } |
| SetterBody.push_back(new (Ctx) CallExpr(Callee, OldValueExpr, true)); |
| |
| // Make sure the didSet/willSet accessors are marked final if in a class. |
| if (!didSet->isFinal() && |
| VD->getDeclContext()->isClassOrClassExtensionContext()) |
| makeFinal(Ctx, didSet); |
| } |
| |
| Set->setBody(BraceStmt::create(Ctx, Loc, SetterBody, Loc, true)); |
| |
| // Type check the body of the getter and setter. |
| TC.typeCheckDecl(Get, true); |
| TC.typeCheckDecl(Get, false); |
| TC.typeCheckDecl(Set, true); |
| TC.typeCheckDecl(Set, false); |
| } |
| |
| static void convertNSManagedStoredVarToComputed(VarDecl *VD, TypeChecker &TC) { |
| assert(VD->getStorageKind() == AbstractStorageDecl::Stored); |
| |
| // Create the getter. |
| auto *Get = createGetterPrototype(VD, TC); |
| |
| // Create the setter. |
| VarDecl *SetValueDecl = nullptr; |
| auto *Set = createSetterPrototype(VD, SetValueDecl, TC); |
| |
| // Okay, we have both the getter and setter. Set them in VD. |
| VD->makeComputed(VD->getLoc(), Get, Set, nullptr, VD->getLoc()); |
| |
| TC.validateDecl(Get); |
| TC.validateDecl(Set); |
| |
| // We've added some members to our containing class/extension, add them to |
| // the members list. |
| addMemberToContextIfNeeded(Get, VD->getDeclContext()); |
| addMemberToContextIfNeeded(Set, VD->getDeclContext()); |
| } |
| |
| 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); |
| } |
| } |
| |
| 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}; } |
| }; |
| } |
| |
| /// Synthesize the getter for an lazy property with the specified storage |
| /// vardecl. |
| static FuncDecl *completeLazyPropertyGetter(VarDecl *VD, VarDecl *Storage, |
| TypeChecker &TC) { |
| auto &Ctx = VD->getASTContext(); |
| |
| // 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 |
| // } |
| auto *Get = VD->getGetter(); |
| TC.validateDecl(Get); |
| |
| SmallVector<ASTNode, 6> Body; |
| |
| // Load the existing storage and store it into the 'tmp1' temporary. |
| auto *Tmp1VD = new (Ctx) VarDecl(/*isStatic*/false, /*isLet*/true,SourceLoc(), |
| Ctx.getIdentifier("tmp1"), Type(), Get); |
| Tmp1VD->setImplicit(); |
| |
| auto *Tmp1PBDPattern = new (Ctx) NamedPattern(Tmp1VD, /*implicit*/true); |
| auto *Tmp1Init = createPropertyLoadOrCallSuperclassGetter(Get, Storage, TC); |
| auto *Tmp1PBD = PatternBindingDecl::create(Ctx, /*StaticLoc*/SourceLoc(), |
| StaticSpellingKind::None, |
| /*VarLoc*/SourceLoc(), |
| Tmp1PBDPattern, Tmp1Init, Get); |
| Body.push_back(Tmp1PBD); |
| Body.push_back(Tmp1VD); |
| |
| // Build the early return inside the if. |
| auto *Tmp1DRE = new (Ctx) DeclRefExpr(Tmp1VD, SourceLoc(), /*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, SourceLoc(), /*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, /*isLet*/true, |
| SourceLoc(), Ctx.getIdentifier("tmp2"), |
| VD->getType(), Get); |
| 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); |
| PBD->setInit(entryIndex, nullptr); |
| PBD->setInitializerChecked(entryIndex); |
| |
| // Recontextualize any closure declcontexts nested in the initializer to |
| // realize that they are in the getter function. |
| InitValue->walk(RecontextualizeClosures(Get)); |
| |
| |
| Pattern *Tmp2PBDPattern = new (Ctx) NamedPattern(Tmp2VD, /*implicit*/true); |
| Tmp2PBDPattern = new (Ctx) TypedPattern(Tmp2PBDPattern, |
| TypeLoc::withoutLoc(VD->getType()), |
| /*implicit*/true); |
| |
| auto *Tmp2PBD = PatternBindingDecl::create(Ctx, /*StaticLoc*/SourceLoc(), |
| StaticSpellingKind::None, |
| InitValue->getStartLoc(), |
| Tmp2PBDPattern, InitValue, Get); |
| Body.push_back(Tmp2PBD); |
| Body.push_back(Tmp2VD); |
| |
| // Assign tmp2 into storage. |
| auto Tmp2DRE = new (Ctx) DeclRefExpr(Tmp2VD, SourceLoc(), /*Implicit*/true, |
| AccessSemantics::DirectToStorage); |
| createPropertyStoreOrCallSuperclassSetter(Get, Tmp2DRE, Storage, Body, TC); |
| |
| // Return tmp2. |
| Tmp2DRE = new (Ctx) DeclRefExpr(Tmp2VD, SourceLoc(), /*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)); |
| |
| return Get; |
| } |
| |
| |
| void TypeChecker::completeLazyVarImplementation(VarDecl *VD) { |
| assert(VD->getAttrs().hasAttribute<LazyAttr>()); |
| assert(VD->getStorageKind() == AbstractStorageDecl::Computed && |
| "variable not validated yet"); |
| 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 = VD->getName().str(); |
| NameBuf += ".storage"; |
| auto StorageName = Context.getIdentifier(NameBuf); |
| auto StorageTy = OptionalType::get(VD->getType()); |
| |
| auto *Storage = new (Context) VarDecl(/*isStatic*/false, /*isLet*/false, |
| VD->getLoc(), StorageName, StorageTy, |
| VD->getDeclContext()); |
| 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 = new (Context) TypedPattern(PBDPattern, |
| TypeLoc::withoutLoc(StorageTy), |
| /*implicit*/true); |
| auto *PBD = PatternBindingDecl::create(Context, /*staticloc*/SourceLoc(), |
| StaticSpellingKind::None, |
| /*varloc*/VD->getLoc(), |
| PBDPattern, /*init*/nullptr, |
| VD->getDeclContext()); |
| PBD->setImplicit(); |
| addMemberToContextIfNeeded(PBD, VD->getDeclContext()); |
| |
| |
| // Now that we've got the storage squared away, synthesize the getter. |
| auto *Get = completeLazyPropertyGetter(VD, Storage, *this); |
| |
| // The setter just forwards on to storage without materializing the initial |
| // value. |
| auto *Set = VD->getSetter(); |
| validateDecl(Set); |
| VarDecl *SetValueDecl = getFirstParamDecl(Set); |
| // FIXME: This is wrong for observed properties. |
| synthesizeTrivialSetter(Set, Storage, SetValueDecl, *this); |
| |
| // 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()->isClassOrClassExtensionContext()) |
| makeFinal(Context, Storage); |
| Storage->setImplicit(); |
| Storage->setAccessibility(Accessibility::Private); |
| Storage->setSetterAccessibility(Accessibility::Private); |
| |
| typeCheckDecl(Get, true); |
| typeCheckDecl(Get, false); |
| |
| typeCheckDecl(Set, true); |
| typeCheckDecl(Set, false); |
| } |
| |
| |
| /// Consider add a materializeForSet accessor to the given storage |
| /// decl (which has accessors). |
| void swift::maybeAddMaterializeForSet(AbstractStorageDecl *storage, |
| TypeChecker &TC) { |
| assert(storage->hasAccessorFunctions()); |
| |
| // Be idempotent. There are a bunch of places where we want to |
| // ensure that there's a materializeForSet accessor. |
| if (storage->getMaterializeForSetFunc()) return; |
| |
| // Never add materializeForSet to readonly declarations. |
| if (!storage->getSetter()) return; |
| |
| // Don't bother if the declaration is invalid. |
| if (storage->isInvalid()) return; |
| |
| // We only need materializeForSet in polymorphic contexts: |
| auto containerTy = |
| storage->getDeclContext()->getDeclaredTypeOfContext(); |
| if (!containerTy) return; |
| |
| NominalTypeDecl *container = containerTy->getAnyNominal(); |
| assert(container && "extension of non-nominal type?"); |
| |
| // - in non-ObjC protocols, but not protocol extensions. |
| if (auto protocol = dyn_cast<ProtocolDecl>(container)) { |
| if (protocol->isObjC()) return; |
| if (storage->getDeclContext()->isProtocolExtensionContext()) return; |
| |
| // - in classes when the storage decl is not final and does |
| // not override a decl that requires a materializeForSet |
| } else if (isa<ClassDecl>(container)) { |
| if (storage->isFinal()) { |
| auto overridden = storage->getOverriddenDecl(); |
| if (!overridden || !overridden->getMaterializeForSetFunc()) |
| return; |
| } |
| |
| // Structs and enums don't need this. |
| } else { |
| assert(isa<StructDecl>(container) || isa<EnumDecl>(container)); |
| return; |
| } |
| |
| addMaterializeForSet(storage, TC); |
| } |
| |
| void swift::maybeAddAccessorsToVariable(VarDecl *var, TypeChecker &TC) { |
| if (var->getGetter() || var->isBeingTypeChecked() || isa<ParamDecl>(var)) |
| return; |
| |
| // Lazy properties get accessors. |
| if (var->getAttrs().hasAttribute<LazyAttr>()) { |
| var->setIsBeingTypeChecked(); |
| |
| auto *getter = createGetterPrototype(var, TC); |
| // lazy getters are mutating on an enclosing value type. |
| if (!var->getDeclContext()->isClassOrClassExtensionContext()) |
| getter->setMutating(); |
| getter->setAccessibility(var->getFormalAccess()); |
| |
| VarDecl *newValueParam = nullptr; |
| auto *setter = createSetterPrototype(var, newValueParam, TC); |
| var->makeComputed(var->getLoc(), getter, setter, nullptr, |
| var->getLoc()); |
| var->setIsBeingTypeChecked(false); |
| |
| TC.validateDecl(getter); |
| TC.validateDecl(setter); |
| |
| addMemberToContextIfNeeded(getter, var->getDeclContext()); |
| addMemberToContextIfNeeded(setter, var->getDeclContext()); |
| return; |
| |
| } |
| // Stored properties in SIL mode don't get auto-synthesized accessors. |
| bool isInSILMode = false; |
| if (auto sourceFile = var->getDeclContext()->getParentSourceFile()) |
| isInSILMode = sourceFile->Kind == SourceFileKind::SIL; |
| |
| auto nominal = var->getDeclContext()->isNominalTypeOrNominalTypeExtensionContext(); |
| if (var->hasAccessorFunctions() || |
| var->isImplicit() || |
| nominal == nullptr) |
| return; |
| |
| // Non-NSManaged class instance variables get accessors, because it affects |
| // vtable layout. |
| if (isa<ClassDecl>(nominal)) { |
| if (var->getAttrs().hasAttribute<NSManagedAttr>()) { |
| var->setIsBeingTypeChecked(); |
| convertNSManagedStoredVarToComputed(var, TC); |
| var->setIsBeingTypeChecked(false); |
| } else if (!isInSILMode) { |
| var->setIsBeingTypeChecked(); |
| addTrivialAccessorsToStorage(var, TC); |
| var->setIsBeingTypeChecked(false); |
| } |
| } |
| |
| // Public instance variables of resilient structs get accessors. |
| if (auto structDecl = dyn_cast<StructDecl>(nominal)) { |
| if (!structDecl->hasFixedLayout() && !isInSILMode) { |
| var->setIsBeingTypeChecked(); |
| addTrivialAccessorsToStorage(var, TC); |
| var->setIsBeingTypeChecked(false); |
| } |
| } |
| } |
| |
| /// \brief 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) { |
| ASTContext &context = tc.Context; |
| SourceLoc Loc = decl->getLoc(); |
| Accessibility accessLevel = decl->getFormalAccess(); |
| if (!decl->hasClangNode()) |
| accessLevel = std::min(accessLevel, Accessibility::Internal); |
| |
| // Determine the parameter type of the implicit constructor. |
| SmallVector<TuplePatternElt, 8> patternElts; |
| SmallVector<Identifier, 8> argNames; |
| if (ICK == ImplicitConstructorKind::Memberwise) { |
| assert(isa<StructDecl>(decl) && "Only struct have memberwise constructor"); |
| |
| // Computed and static properties are not initialized. |
| for (auto var : decl->getStoredProperties()) { |
| if (var->isImplicit()) |
| continue; |
| tc.validateDecl(var); |
| |
| |
| // 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()); |
| |
| auto varType = tc.getTypeOfRValue(var); |
| |
| // 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>()) |
| varType = OptionalType::get(varType); |
| |
| // Create the parameter. |
| auto *arg = new (context) ParamDecl(/*IsLet*/true, Loc, var->getName(), |
| Loc, var->getName(), varType, decl); |
| arg->setImplicit(); |
| argNames.push_back(var->getName()); |
| Pattern *pattern = new (context) NamedPattern(arg); |
| pattern->setImplicit(); |
| TypeLoc tyLoc = TypeLoc::withoutLoc(varType); |
| pattern = new (context) TypedPattern(pattern, tyLoc); |
| patternElts.push_back(TuplePatternElt(var->getName(), SourceLoc(), |
| pattern, false)); |
| } |
| } |
| |
| auto pattern = TuplePattern::create(context, Loc, patternElts, Loc); |
| pattern->setImplicit(); |
| |
| // Create the constructor. |
| DeclName name(context, context.Id_init, argNames); |
| Pattern *selfPat = buildImplicitSelfParameter(Loc, decl); |
| auto *ctor = new (context) ConstructorDecl(name, Loc, OTK_None, SourceLoc(), |
| selfPat, pattern, |
| nullptr, SourceLoc(), decl); |
| |
| // Mark implicit. |
| ctor->setImplicit(); |
| ctor->setAccessibility(accessLevel); |
| |
| if (ICK == ImplicitConstructorKind::Memberwise) |
| ctor->setIsMemberwiseInitializer(); |
| |
| // 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 (tc.Context) OverrideAttr(/*implicit=*/true)); |
| } |
| |
| // Type-check the constructor declaration. |
| tc.typeCheckDecl(ctor, /*isFirstPass=*/true); |
| |
| // If the struct in which this constructor is being added was imported, |
| // add it as an external definition. |
| if (decl->hasClangNode()) { |
| tc.Context.addedExternalDecl(ctor); |
| } |
| |
| return ctor; |
| } |
| |
| /// Create an expression that references the variables in the given |
| /// pattern for, e.g., forwarding of these variables to another |
| /// function with the same signature. |
| static Expr *forwardArguments(TypeChecker &tc, ClassDecl *classDecl, |
| ConstructorDecl *toDecl, |
| Pattern *bodyPattern, |
| ArrayRef<Identifier> argumentNames) { |
| switch (bodyPattern->getKind()) { |
| #define PATTERN(Id, Parent) |
| #define REFUTABLE_PATTERN(Id, Parent) case PatternKind::Id: |
| #include "swift/AST/PatternNodes.def" |
| return nullptr; |
| |
| case PatternKind::Paren: { |
| auto subExpr = forwardArguments(tc, classDecl, toDecl, |
| cast<ParenPattern>(bodyPattern)->getSubPattern(), |
| { }); |
| if (!subExpr) return nullptr; |
| |
| // If there is a name for this single-argument thing, then form a tupleexpr. |
| if (argumentNames.size() != 1 || argumentNames[0].empty()) |
| return new (tc.Context) ParenExpr(SourceLoc(), subExpr, SourceLoc(), |
| /*hasTrailingClosure=*/false); |
| |
| return TupleExpr::createImplicit(tc.Context, subExpr, argumentNames); |
| } |
| |
| |
| case PatternKind::Tuple: { |
| auto bodyTuple = cast<TuplePattern>(bodyPattern); |
| SmallVector<Expr *, 4> values; |
| |
| // FIXME: Can't forward varargs yet. |
| if (bodyTuple->hasAnyEllipsis()) { |
| tc.diagnose(classDecl->getLoc(), |
| diag::unsupported_synthesize_init_variadic, |
| classDecl->getDeclaredType()); |
| tc.diagnose(toDecl, diag::variadic_superclass_init_here); |
| return nullptr; |
| } |
| |
| for (unsigned i = 0, n = bodyTuple->getNumElements(); i != n; ++i) { |
| // Forward the value. |
| auto subExpr = forwardArguments(tc, classDecl, toDecl, |
| bodyTuple->getElement(i).getPattern(), |
| { }); |
| if (!subExpr) |
| return nullptr; |
| values.push_back(subExpr); |
| |
| // Dig out the name. |
| auto subPattern = bodyTuple->getElement(i).getPattern(); |
| do { |
| if (auto typed = dyn_cast<TypedPattern>(subPattern)) { |
| subPattern = typed->getSubPattern(); |
| continue; |
| } |
| |
| if (auto paren = dyn_cast<ParenPattern>(subPattern)) { |
| subPattern = paren->getSubPattern(); |
| continue; |
| } |
| |
| break; |
| } while (true); |
| } |
| |
| if (values.size() == 1 && |
| (argumentNames.empty() || argumentNames[0].empty())) |
| return new (tc.Context) ParenExpr(SourceLoc(), values[0], SourceLoc(), |
| /*hasTrailingClosure=*/false); |
| |
| return TupleExpr::createImplicit(tc.Context, values, argumentNames); |
| } |
| |
| case PatternKind::Any: |
| case PatternKind::Named: { |
| auto decl = cast<NamedPattern>(bodyPattern)->getDecl(); |
| Expr *declRef = new (tc.Context) DeclRefExpr(decl, SourceLoc(), |
| /*Implicit=*/true); |
| if (decl->getType()->is<InOutType>()) |
| declRef = new (tc.Context) InOutExpr(SourceLoc(), declRef, |
| Type(), /*isImplicit=*/true); |
| return declRef; |
| } |
| |
| case PatternKind::Typed: |
| return forwardArguments(tc, classDecl, toDecl, |
| cast<TypedPattern>(bodyPattern)->getSubPattern(), |
| argumentNames); |
| |
| case PatternKind::Var: |
| return forwardArguments(tc, classDecl, toDecl, |
| cast<VarPattern>(bodyPattern)->getSubPattern(), |
| argumentNames); |
| |
| } |
| } |
| |
| /// Create a stub body that emits a fatal error message. |
| static void createStubBody(TypeChecker &tc, ConstructorDecl *ctor) { |
| auto unimplementedInitDecl = tc.Context.getUnimplementedInitializerDecl(&tc); |
| auto classDecl = ctor->getExtensionType()->getClassOrBoundGenericClass(); |
| if (!unimplementedInitDecl) { |
| tc.diagnose(classDecl->getLoc(), diag::missing_unimplemented_init_runtime); |
| return; |
| } |
| |
| // Create a call to Swift._unimplemented_initializer |
| auto loc = classDecl->getLoc(); |
| Expr *fn = new (tc.Context) DeclRefExpr(unimplementedInitDecl, loc, |
| /*Implicit=*/true); |
| |
| llvm::SmallString<64> buffer; |
| StringRef fullClassName = tc.Context.AllocateCopy( |
| (classDecl->getModuleContext()->getName().str() + |
| "." + |
| classDecl->getName().str()).toStringRef(buffer)); |
| |
| Expr *className = new (tc.Context) StringLiteralExpr(fullClassName, loc); |
| className = new (tc.Context) ParenExpr(loc, className, loc, false); |
| Expr *call = new (tc.Context) CallExpr(fn, className, /*Implicit=*/true); |
| ctor->setBody(BraceStmt::create(tc.Context, SourceLoc(), |
| ASTNode(call), |
| SourceLoc(), |
| /*Implicit=*/true)); |
| |
| // Note that this is a stub implementation. |
| ctor->setStubImplementation(true); |
| } |
| |
| ConstructorDecl * |
| swift::createDesignatedInitOverride(TypeChecker &tc, |
| ClassDecl *classDecl, |
| ConstructorDecl *superclassCtor, |
| DesignatedInitKind kind) { |
| // Determine the initializer parameters. |
| Type superInitType = superclassCtor->getInitializerInterfaceType(); |
| if (superInitType->is<GenericFunctionType>() || |
| classDecl->getGenericParamsOfContext()) { |
| // FIXME: Handle generic initializers as well. |
| return nullptr; |
| } |
| |
| auto &ctx = tc.Context; |
| |
| // Create the 'self' declaration and patterns. |
| auto *selfDecl = new (ctx) ParamDecl(/*IsLet*/ true, |
| SourceLoc(), Identifier(), |
| SourceLoc(), ctx.Id_self, |
| Type(), classDecl); |
| selfDecl->setImplicit(); |
| Pattern *selfBodyPattern |
| = new (ctx) NamedPattern(selfDecl, /*Implicit=*/true); |
| selfBodyPattern = new (ctx) TypedPattern(selfBodyPattern, TypeLoc()); |
| |
| // Create the initializer parameter patterns. |
| OptionSet<Pattern::CloneFlags> options = Pattern::Implicit; |
| options |= Pattern::Inherited; |
| Pattern *bodyParamPatterns |
| = superclassCtor->getBodyParamPatterns()[1]->clone(ctx, options); |
| |
| // Fix up the default arguments in the type to refer to inherited default |
| // arguments. |
| // FIXME: If we weren't cloning the type along with the pattern, this would be |
| // a lot more direct. |
| Type argType = bodyParamPatterns->getType(); |
| |
| // Local function that maps default arguments to inherited default arguments. |
| std::function<Type(Type)> inheritDefaultArgs = [&](Type type) -> Type { |
| auto tuple = type->getAs<TupleType>(); |
| if (!tuple) |
| return type; |
| |
| bool anyChanged = false; |
| SmallVector<TupleTypeElt, 4> elements; |
| unsigned index = 0; |
| for (const auto &elt : tuple->getElements()) { |
| Type eltTy = elt.getType().transform(inheritDefaultArgs); |
| if (!eltTy) |
| return Type(); |
| |
| // If nothing has changed, just keep going. |
| if (!anyChanged && eltTy.getPointer() == elt.getType().getPointer() && |
| (elt.getDefaultArgKind() == DefaultArgumentKind::None || |
| elt.getDefaultArgKind() == DefaultArgumentKind::Inherited)) { |
| ++index; |
| continue; |
| } |
| |
| // If this is the first change we've seen, copy all of the previous |
| // elements. |
| if (!anyChanged) { |
| // Copy all of the previous elements. |
| for (unsigned i = 0; i != index; ++i) { |
| const TupleTypeElt &FromElt = tuple->getElement(i); |
| elements.push_back(TupleTypeElt(FromElt.getType(), FromElt.getName(), |
| FromElt.getDefaultArgKind(), |
| FromElt.isVararg())); |
| } |
| |
| anyChanged = true; |
| } |
| |
| // Add the new tuple element, with the new type, no initializer, |
| auto defaultArgKind = elt.getDefaultArgKind(); |
| if (defaultArgKind != DefaultArgumentKind::None) |
| defaultArgKind = DefaultArgumentKind::Inherited; |
| elements.push_back(TupleTypeElt(eltTy, elt.getName(), defaultArgKind, |
| elt.isVararg())); |
| ++index; |
| } |
| |
| if (!anyChanged) |
| return type; |
| |
| return TupleType::get(elements, ctx); |
| }; |
| |
| argType = argType.transform(inheritDefaultArgs); |
| bodyParamPatterns->setType(argType); |
| |
| // Create the initializer declaration. |
| auto ctor = new (ctx) ConstructorDecl(superclassCtor->getFullName(), |
| classDecl->getBraces().Start, |
| superclassCtor->getFailability(), |
| SourceLoc(), |
| selfBodyPattern, bodyParamPatterns, |
| nullptr, SourceLoc(), classDecl); |
| ctor->setImplicit(); |
| ctor->setAccessibility(std::min(classDecl->getFormalAccess(), |
| superclassCtor->getFormalAccess())); |
| |
| // Make sure the constructor is only as available as its superclass's |
| // constructor. |
| AvailabilityInference::applyInferredAvailableAttrs(ctor, superclassCtor, |
| ctx); |
| |
| // Configure 'self'. |
| Type selfType = configureImplicitSelf(tc, ctor); |
| selfBodyPattern->setType(selfType); |
| cast<TypedPattern>(selfBodyPattern)->getSubPattern()->setType(selfType); |
| |
| // Set the type of the initializer. |
| configureConstructorType(ctor, selfType, |
| bodyParamPatterns->getType(), |
| superclassCtor->isBodyThrowing()); |
| if (superclassCtor->isObjC()) { |
| auto errorConvention = superclassCtor->getForeignErrorConvention(); |
| markAsObjC(tc, ctor, ObjCReason::ImplicitlyObjC, errorConvention); |
| |
| // Inherit the @objc name from the superclass initializer, if it |
| // has one. |
| if (auto objcAttr = superclassCtor->getAttrs().getAttribute<ObjCAttr>()) { |
| if (objcAttr->hasName()) { |
| auto *clonedAttr = objcAttr->clone(ctx); |
| // Set it to implicit to disable printing it for SIL. |
| clonedAttr->setImplicit(true); |
| ctor->getAttrs().add(clonedAttr); |
| } |
| } |
| } |
| if (superclassCtor->isRequired()) |
| ctor->getAttrs().add(new (tc.Context) RequiredAttr(/*implicit=*/true)); |
| |
| // Wire up the overrides. |
| ctor->getAttrs().add(new (tc.Context) OverrideAttr(/*Implicit=*/true)); |
| checkOverrides(tc, ctor); |
| |
| if (kind == DesignatedInitKind::Stub) { |
| // Make this a stub implementation. |
| createStubBody(tc, ctor); |
| return ctor; |
| } |
| |
| // Form the body of a chaining designated initializer. |
| assert(kind == DesignatedInitKind::Chaining); |
| |
| // Reference to super.init. |
| Expr *superRef = new (ctx) SuperRefExpr(selfDecl, SourceLoc(), |
| /*Implicit=*/true); |
| Expr *ctorRef = new (ctx) UnresolvedConstructorExpr(superRef, |
| SourceLoc(), |
| SourceLoc(), |
| /*Implicit=*/true); |
| |
| Expr *ctorArgs = forwardArguments(tc, classDecl, superclassCtor, |
| ctor->getBodyParamPatterns()[1], |
| ctor->getFullName().getArgumentNames()); |
| if (!ctorArgs) { |
| // FIXME: We should be able to assert that this never happens, |
| // but there are currently holes when dealing with vararg |
| // initializers and _ parameters. Fail somewhat gracefully by |
| // generating a stub here. |
| createStubBody(tc, ctor); |
| return ctor; |
| } |
| |
| Expr *superCall = new (ctx) CallExpr(ctorRef, ctorArgs, /*Implicit=*/true); |
| if (superclassCtor->isBodyThrowing()) { |
| superCall = new (ctx) TryExpr(SourceLoc(), superCall, Type(), |
| /*Implicit=*/true); |
| } |
| ctor->setBody(BraceStmt::create(tc.Context, SourceLoc(), |
| ASTNode(superCall), |
| SourceLoc(), |
| /*Implicit=*/true)); |
| |
| return ctor; |
| } |
| |
| void TypeChecker::addImplicitDestructor(ClassDecl *CD) { |
| if (CD->hasDestructor() || CD->isInvalid()) |
| return; |
| |
| Pattern *selfPat = buildImplicitSelfParameter(CD->getLoc(), CD); |
| |
| auto *DD = new (Context) DestructorDecl(Context.Id_deinit, CD->getLoc(), |
| selfPat, CD); |
| |
| DD->setImplicit(); |
| |
| // Type-check the destructor declaration. |
| typeCheckDecl(DD, /*isFirstPass=*/true); |
| |
| // Create an empty body for the destructor. |
| DD->setBody(BraceStmt::create(Context, CD->getLoc(), { }, CD->getLoc(),true)); |
| CD->addMember(DD); |
| CD->setHasDestructor(); |
| } |