| //===--- SILGenConstructor.cpp - SILGen for constructors ------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "ArgumentSource.h" |
| #include "Conversion.h" |
| #include "Initialization.h" |
| #include "LValue.h" |
| #include "RValue.h" |
| #include "SILGenFunction.h" |
| #include "SILGenFunctionBuilder.h" |
| #include "Scope.h" |
| #include "swift/AST/ASTMangler.h" |
| #include "swift/AST/ForeignErrorConvention.h" |
| #include "swift/AST/GenericEnvironment.h" |
| #include "swift/AST/ParameterList.h" |
| #include "swift/AST/PropertyWrappers.h" |
| #include "swift/Basic/Defer.h" |
| #include "swift/SIL/SILArgument.h" |
| #include "swift/SIL/SILUndef.h" |
| #include "swift/SIL/TypeLowering.h" |
| |
| using namespace swift; |
| using namespace Lowering; |
| |
| static SILValue emitConstructorMetatypeArg(SILGenFunction &SGF, |
| ValueDecl *ctor) { |
| // In addition to the declared arguments, the constructor implicitly takes |
| // the metatype as its first argument, like a static function. |
| auto ctorFnType = ctor->getInterfaceType()->castTo<AnyFunctionType>(); |
| assert(ctorFnType->getParams().size() == 1 && |
| "more than one self parameter?"); |
| auto param = ctorFnType->getParams()[0]; |
| assert(!param.isVariadic() && !param.isInOut()); |
| Type metatype = param.getPlainType(); |
| auto *DC = ctor->getInnermostDeclContext(); |
| auto &AC = SGF.getASTContext(); |
| auto VD = |
| new (AC) ParamDecl(SourceLoc(), SourceLoc(), |
| AC.getIdentifier("$metatype"), SourceLoc(), |
| AC.getIdentifier("$metatype"), DC); |
| VD->setSpecifier(ParamSpecifier::Default); |
| VD->setInterfaceType(metatype); |
| |
| SGF.AllocatorMetatype = SGF.F.begin()->createFunctionArgument( |
| SGF.getLoweredTypeForFunctionArgument(DC->mapTypeIntoContext(metatype)), |
| VD); |
| |
| return SGF.AllocatorMetatype; |
| } |
| |
| // FIXME: Consolidate this with SILGenProlog |
| static RValue emitImplicitValueConstructorArg(SILGenFunction &SGF, |
| SILLocation loc, |
| CanType interfaceType, |
| DeclContext *DC) { |
| auto type = DC->mapTypeIntoContext(interfaceType)->getCanonicalType(); |
| |
| // Restructure tuple arguments. |
| if (auto tupleTy = dyn_cast<TupleType>(interfaceType)) { |
| RValue tuple(type); |
| for (auto fieldType : tupleTy.getElementTypes()) |
| tuple.addElement(emitImplicitValueConstructorArg(SGF, loc, fieldType, DC)); |
| return tuple; |
| } |
| |
| auto &AC = SGF.getASTContext(); |
| auto VD = new (AC) ParamDecl(SourceLoc(), SourceLoc(), |
| AC.getIdentifier("$implicit_value"), |
| SourceLoc(), |
| AC.getIdentifier("$implicit_value"), |
| DC); |
| VD->setSpecifier(ParamSpecifier::Default); |
| VD->setInterfaceType(interfaceType); |
| |
| auto argType = SGF.getLoweredTypeForFunctionArgument(type); |
| auto *arg = SGF.F.begin()->createFunctionArgument(argType, VD); |
| ManagedValue mvArg; |
| if (arg->getArgumentConvention().isOwnedConvention()) { |
| mvArg = SGF.emitManagedRValueWithCleanup(arg); |
| } else { |
| mvArg = ManagedValue::forUnmanaged(arg); |
| } |
| |
| // This can happen if the value is resilient in the calling convention |
| // but not resilient locally. |
| if (argType.isLoadable(SGF.F) && argType.isAddress()) { |
| if (mvArg.isPlusOne(SGF)) |
| mvArg = SGF.B.createLoadTake(loc, mvArg); |
| else |
| mvArg = SGF.B.createLoadBorrow(loc, mvArg); |
| } |
| |
| return RValue(SGF, loc, type, mvArg); |
| } |
| |
| /// If the field has a property wrapper for which we will need to call the |
| /// wrapper type's init(wrappedValue:, ...), call the function that performs |
| /// that initialization and return the result. Otherwise, return \c arg. |
| static RValue maybeEmitPropertyWrapperInitFromValue( |
| SILGenFunction &SGF, |
| SILLocation loc, |
| VarDecl *field, |
| SubstitutionMap subs, |
| RValue &&arg) { |
| auto originalProperty = field->getOriginalWrappedProperty(); |
| if (!originalProperty || |
| !originalProperty->isPropertyMemberwiseInitializedWithWrappedType()) |
| return std::move(arg); |
| |
| auto wrapperInfo = originalProperty->getPropertyWrapperBackingPropertyInfo(); |
| if (!wrapperInfo || !wrapperInfo.initializeFromOriginal) |
| return std::move(arg); |
| |
| return SGF.emitApplyOfPropertyWrapperBackingInitializer(loc, originalProperty, |
| subs, std::move(arg)); |
| } |
| |
| static SubstitutionMap getSubstitutionsForPropertyInitializer( |
| DeclContext *dc, |
| NominalTypeDecl *nominal) { |
| // We want a substitution list written in terms of the generic |
| // signature of the type, with replacement archetypes from the |
| // constructor's context (which might be in an extension of |
| // the type, which adds additional generic requirements). |
| if (auto *genericEnv = dc->getGenericEnvironmentOfContext()) { |
| // Generate a set of substitutions for the initialization function, |
| // whose generic signature is that of the type context, and whose |
| // replacement types are the archetypes of the initializer itself. |
| return SubstitutionMap::get( |
| nominal->getGenericSignatureOfContext(), |
| [&](SubstitutableType *type) { |
| if (auto gp = type->getAs<GenericTypeParamType>()) { |
| return genericEnv->mapTypeIntoContext(gp); |
| } |
| |
| return Type(type); |
| }, |
| LookUpConformanceInModule(dc->getParentModule())); |
| } |
| |
| return SubstitutionMap(); |
| } |
| |
| static void emitImplicitValueConstructor(SILGenFunction &SGF, |
| ConstructorDecl *ctor) { |
| RegularLocation Loc(ctor); |
| Loc.markAutoGenerated(); |
| |
| AssertingManualScope functionLevelScope(SGF.Cleanups, |
| CleanupLocation::get(Loc)); |
| |
| // FIXME: Handle 'self' along with the other arguments. |
| auto *paramList = ctor->getParameters(); |
| auto *selfDecl = ctor->getImplicitSelfDecl(); |
| auto selfIfaceTy = selfDecl->getInterfaceType(); |
| SILType selfTy = SGF.getLoweredTypeForFunctionArgument(selfDecl->getType()); |
| |
| // Emit the indirect return argument, if any. |
| SILValue resultSlot; |
| if (SILModuleConventions::isReturnedIndirectlyInSIL(selfTy, SGF.SGM.M)) { |
| auto &AC = SGF.getASTContext(); |
| auto VD = new (AC) ParamDecl(SourceLoc(), SourceLoc(), |
| AC.getIdentifier("$return_value"), |
| SourceLoc(), |
| AC.getIdentifier("$return_value"), |
| ctor); |
| VD->setSpecifier(ParamSpecifier::InOut); |
| VD->setInterfaceType(selfIfaceTy); |
| resultSlot = |
| SGF.F.begin()->createFunctionArgument(selfTy.getAddressType(), VD); |
| } |
| |
| // Emit the elementwise arguments. |
| SmallVector<RValue, 4> elements; |
| for (size_t i = 0, size = paramList->size(); i < size; ++i) { |
| auto ¶m = paramList->get(i); |
| |
| elements.push_back( |
| emitImplicitValueConstructorArg( |
| SGF, Loc, param->getInterfaceType()->getCanonicalType(), ctor)); |
| } |
| |
| emitConstructorMetatypeArg(SGF, ctor); |
| |
| auto *decl = selfTy.getStructOrBoundGenericStruct(); |
| assert(decl && "not a struct?!"); |
| |
| auto subs = getSubstitutionsForPropertyInitializer(decl, decl); |
| |
| // If we have an indirect return slot, initialize it in-place. |
| if (resultSlot) { |
| auto elti = elements.begin(), eltEnd = elements.end(); |
| for (VarDecl *field : decl->getStoredProperties()) { |
| auto fieldTy = |
| selfTy.getFieldType(field, SGF.SGM.M, SGF.getTypeExpansionContext()); |
| SILValue slot = |
| SGF.B.createStructElementAddr(Loc, resultSlot, field, |
| fieldTy.getAddressType()); |
| InitializationPtr init(new KnownAddressInitialization(slot)); |
| |
| // If it's memberwise initialized, do so now. |
| if (field->isMemberwiseInitialized(/*preferDeclaredProperties=*/false)) { |
| assert(elti != eltEnd && |
| "number of args does not match number of fields"); |
| (void)eltEnd; |
| FullExpr scope(SGF.Cleanups, field->getParentPatternBinding()); |
| |
| RValue arg = std::move(*elti); |
| |
| // If the stored property has an attached result builder and its |
| // type is not a function type, the argument is a noescape closure |
| // that needs to be called. |
| if (field->getResultBuilderType()) { |
| if (!field->getValueInterfaceType() |
| ->lookThroughAllOptionalTypes()->is<AnyFunctionType>()) { |
| auto resultTy = cast<FunctionType>(arg.getType()).getResult(); |
| arg = SGF.emitMonomorphicApply( |
| Loc, std::move(arg).getAsSingleValue(SGF, Loc), { }, resultTy, |
| resultTy, ApplyOptions::None, None, None); |
| } |
| } |
| |
| maybeEmitPropertyWrapperInitFromValue(SGF, Loc, field, subs, |
| std::move(arg)) |
| .forwardInto(SGF, Loc, init.get()); |
| ++elti; |
| } else { |
| assert(field->getType()->getReferenceStorageReferent()->isEqual( |
| field->getParentInitializer()->getType()) && |
| "Initialization of field with mismatched type!"); |
| |
| // Cleanup after this initialization. |
| FullExpr scope(SGF.Cleanups, field->getParentPatternBinding()); |
| |
| // If this is a property wrapper backing storage var that isn't |
| // memberwise initialized and has an original wrapped value, apply |
| // the property wrapper backing initializer. |
| if (auto *wrappedVar = field->getOriginalWrappedProperty()) { |
| auto wrappedInfo = wrappedVar->getPropertyWrapperBackingPropertyInfo(); |
| auto *placeholder = wrappedInfo.wrappedValuePlaceholder; |
| if (placeholder && placeholder->getOriginalWrappedValue()) { |
| auto arg = SGF.emitRValue(placeholder->getOriginalWrappedValue()); |
| maybeEmitPropertyWrapperInitFromValue(SGF, Loc, field, subs, |
| std::move(arg)) |
| .forwardInto(SGF, Loc, init.get()); |
| continue; |
| } |
| } |
| |
| SGF.emitExprInto(field->getParentInitializer(), init.get()); |
| } |
| } |
| |
| SGF.B.createReturn(ImplicitReturnLocation::getImplicitReturnLoc(Loc), |
| SGF.emitEmptyTuple(Loc), std::move(functionLevelScope)); |
| return; |
| } |
| |
| // Otherwise, build a struct value directly from the elements. |
| SmallVector<SILValue, 4> eltValues; |
| |
| auto elti = elements.begin(), eltEnd = elements.end(); |
| for (VarDecl *field : decl->getStoredProperties()) { |
| auto fieldTy = |
| selfTy.getFieldType(field, SGF.SGM.M, SGF.getTypeExpansionContext()); |
| RValue value; |
| |
| FullExpr scope(SGF.Cleanups, field->getParentPatternBinding()); |
| |
| // If it's memberwise initialized, do so now. |
| if (field->isMemberwiseInitialized(/*preferDeclaredProperties=*/false)) { |
| assert(elti != eltEnd && "number of args does not match number of fields"); |
| (void)eltEnd; |
| value = std::move(*elti); |
| ++elti; |
| } else { |
| // Otherwise, use its initializer. |
| assert(field->isParentInitialized()); |
| Expr *init = field->getParentInitializer(); |
| |
| // If this is a property wrapper backing storage var that isn't |
| // memberwise initialized, use the original wrapped value if it exists. |
| if (auto *wrappedVar = field->getOriginalWrappedProperty()) { |
| auto wrappedInfo = wrappedVar->getPropertyWrapperBackingPropertyInfo(); |
| auto *placeholder = wrappedInfo.wrappedValuePlaceholder; |
| if (placeholder && placeholder->getOriginalWrappedValue()) { |
| init = placeholder->getOriginalWrappedValue(); |
| } |
| } |
| |
| value = SGF.emitRValue(init); |
| } |
| |
| // Cleanup after this initialization. |
| SILValue v = maybeEmitPropertyWrapperInitFromValue(SGF, Loc, field, subs, |
| std::move(value)) |
| .forwardAsSingleStorageValue(SGF, fieldTy, Loc); |
| |
| eltValues.push_back(v); |
| } |
| |
| SILValue selfValue = SGF.B.createStruct(Loc, selfTy, eltValues); |
| SGF.B.createReturn(ImplicitReturnLocation::getImplicitReturnLoc(Loc), |
| selfValue, std::move(functionLevelScope)); |
| return; |
| } |
| |
| void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) { |
| MagicFunctionName = SILGenModule::getMagicFunctionName(ctor); |
| |
| if (ctor->isMemberwiseInitializer()) |
| return emitImplicitValueConstructor(*this, ctor); |
| |
| // True if this constructor delegates to a peer constructor with self.init(). |
| bool isDelegating = ctor->getDelegatingOrChainedInitKind().initKind == |
| BodyInitKind::Delegating; |
| |
| // Get the 'self' decl and type. |
| VarDecl *selfDecl = ctor->getImplicitSelfDecl(); |
| auto &lowering = getTypeLowering(selfDecl->getType()); |
| |
| // Decide if we need to do extra work to warn on unsafe behavior in pre-Swift-5 |
| // modes. |
| MarkUninitializedInst::Kind MUIKind; |
| if (isDelegating) { |
| MUIKind = MarkUninitializedInst::DelegatingSelf; |
| } else if (getASTContext().isSwiftVersionAtLeast(5)) { |
| MUIKind = MarkUninitializedInst::RootSelf; |
| } else { |
| auto *dc = ctor->getParent(); |
| if (isa<ExtensionDecl>(dc) && |
| dc->getSelfStructDecl()->getParentModule() != dc->getParentModule()) { |
| MUIKind = MarkUninitializedInst::CrossModuleRootSelf; |
| } else { |
| MUIKind = MarkUninitializedInst::RootSelf; |
| } |
| } |
| |
| // Allocate the local variable for 'self'. |
| emitLocalVariableWithCleanup(selfDecl, MUIKind)->finishInitialization(*this); |
| SILValue selfLV = VarLocs[selfDecl].value; |
| |
| // Emit the prolog. |
| emitProlog(ctor->getParameters(), |
| /*selfParam=*/nullptr, |
| ctor->getResultInterfaceType(), ctor, |
| ctor->hasThrows(), |
| ctor->getThrowsLoc()); |
| emitConstructorMetatypeArg(*this, ctor); |
| |
| // Create a basic block to jump to for the implicit 'self' return. |
| // We won't emit this until after we've emitted the body. |
| // The epilog takes a void return because the return of 'self' is implicit. |
| prepareEpilog(false, ctor->hasThrows(), CleanupLocation(ctor)); |
| |
| // If the constructor can fail, set up an alternative epilog for constructor |
| // failure. |
| SILBasicBlock *failureExitBB = nullptr; |
| SILArgument *failureExitArg = nullptr; |
| auto resultType = ctor->mapTypeIntoContext(ctor->getResultInterfaceType()); |
| auto &resultLowering = getTypeLowering(resultType); |
| |
| if (ctor->isFailable()) { |
| SILBasicBlock *failureBB = createBasicBlock(FunctionSection::Postmatter); |
| |
| // On failure, we'll clean up everything (except self, which should have |
| // been cleaned up before jumping here) and return nil instead. |
| SILGenSavedInsertionPoint savedIP(*this, failureBB, |
| FunctionSection::Postmatter); |
| failureExitBB = createBasicBlock(); |
| Cleanups.emitCleanupsForReturn(ctor, IsForUnwind); |
| // Return nil. |
| if (F.getConventions().hasIndirectSILResults()) { |
| // Inject 'nil' into the indirect return. |
| assert(F.getIndirectResults().size() == 1); |
| B.createInjectEnumAddr(ctor, F.getIndirectResults()[0], |
| getASTContext().getOptionalNoneDecl()); |
| B.createBranch(ctor, failureExitBB); |
| |
| B.setInsertionPoint(failureExitBB); |
| B.createReturn(ctor, emitEmptyTuple(ctor)); |
| } else { |
| // Pass 'nil' as the return value to the exit BB. |
| failureExitArg = failureExitBB->createPhiArgument( |
| resultLowering.getLoweredType(), OwnershipKind::Owned); |
| SILValue nilResult = |
| B.createEnum(ctor, SILValue(), getASTContext().getOptionalNoneDecl(), |
| resultLowering.getLoweredType()); |
| B.createBranch(ctor, failureExitBB, nilResult); |
| |
| B.setInsertionPoint(failureExitBB); |
| B.createReturn(ctor, failureExitArg); |
| } |
| |
| FailDest = JumpDest(failureBB, Cleanups.getCleanupsDepth(), ctor); |
| } |
| |
| // If this is not a delegating constructor, emit member initializers. |
| if (!isDelegating) { |
| auto *typeDC = ctor->getDeclContext(); |
| auto *nominal = typeDC->getSelfNominalTypeDecl(); |
| emitMemberInitializers(ctor, selfDecl, nominal); |
| } |
| |
| emitProfilerIncrement(ctor->getTypecheckedBody()); |
| // Emit the constructor body. |
| emitStmt(ctor->getTypecheckedBody()); |
| |
| |
| // Build a custom epilog block, since the AST representation of the |
| // constructor decl (which has no self in the return type) doesn't match the |
| // SIL representation. |
| SILValue selfValue; |
| { |
| SILGenSavedInsertionPoint savedIP(*this, ReturnDest.getBlock()); |
| assert(B.getInsertionBB()->empty() && "Epilog already set up?"); |
| |
| auto cleanupLoc = CleanupLocation::get(ctor); |
| |
| if (!F.getConventions().hasIndirectSILResults()) { |
| // Otherwise, load and return the final 'self' value. |
| selfValue = lowering.emitLoad(B, cleanupLoc, selfLV, |
| LoadOwnershipQualifier::Copy); |
| |
| // Inject the self value into an optional if the constructor is failable. |
| if (ctor->isFailable()) { |
| selfValue = B.createEnum(cleanupLoc, selfValue, |
| getASTContext().getOptionalSomeDecl(), |
| getLoweredLoadableType(resultType)); |
| } |
| } else { |
| // If 'self' is address-only, copy 'self' into the indirect return slot. |
| assert(F.getConventions().getNumIndirectSILResults() == 1 |
| && "no indirect return for address-only ctor?!"); |
| |
| // Get the address to which to store the result. |
| SILValue completeReturnAddress = F.getIndirectResults()[0]; |
| SILValue returnAddress; |
| if (!ctor->isFailable()) { |
| // For non-failable initializers, store to the return address directly. |
| returnAddress = completeReturnAddress; |
| } else { |
| // If this is a failable initializer, project out the payload. |
| returnAddress = B.createInitEnumDataAddr(cleanupLoc, |
| completeReturnAddress, |
| getASTContext().getOptionalSomeDecl(), |
| selfLV->getType()); |
| } |
| |
| // We have to do a non-take copy because someone else may be using the |
| // box (e.g. someone could have closed over it). |
| B.createCopyAddr(cleanupLoc, selfLV, returnAddress, |
| IsNotTake, IsInitialization); |
| |
| // Inject the enum tag if the result is optional because of failability. |
| if (ctor->isFailable()) { |
| // Inject the 'Some' tag. |
| B.createInjectEnumAddr(cleanupLoc, completeReturnAddress, |
| getASTContext().getOptionalSomeDecl()); |
| } |
| } |
| } |
| |
| // Finally, emit the epilog and post-matter. |
| auto returnLoc = emitEpilog(ctor, /*UsesCustomEpilog*/true); |
| |
| // Finish off the epilog by returning. If this is a failable ctor, then we |
| // actually jump to the failure epilog to keep the invariant that there is |
| // only one SIL return instruction per SIL function. |
| if (B.hasValidInsertionPoint()) { |
| if (!failureExitBB) { |
| // If we're not returning self, then return () since we're returning Void. |
| if (!selfValue) { |
| SILLocation loc(ctor); |
| loc.markAutoGenerated(); |
| selfValue = emitEmptyTuple(loc); |
| } |
| |
| B.createReturn(returnLoc, selfValue); |
| } else { |
| if (selfValue) |
| B.createBranch(returnLoc, failureExitBB, selfValue); |
| else |
| B.createBranch(returnLoc, failureExitBB); |
| } |
| } |
| } |
| |
| void SILGenFunction::emitEnumConstructor(EnumElementDecl *element) { |
| Type enumIfaceTy = element->getParentEnum()->getDeclaredInterfaceType(); |
| Type enumTy = F.mapTypeIntoContext(enumIfaceTy); |
| auto &enumTI = |
| SGM.Types.getTypeLowering(enumTy, TypeExpansionContext::minimal()); |
| |
| RegularLocation Loc(element); |
| CleanupLocation CleanupLoc(element); |
| Loc.markAutoGenerated(); |
| |
| // Emit the indirect return slot. |
| std::unique_ptr<Initialization> dest; |
| if (enumTI.isAddressOnly() && silConv.useLoweredAddresses()) { |
| auto &AC = getASTContext(); |
| auto VD = new (AC) ParamDecl(SourceLoc(), SourceLoc(), |
| AC.getIdentifier("$return_value"), |
| SourceLoc(), |
| AC.getIdentifier("$return_value"), |
| element->getDeclContext()); |
| VD->setSpecifier(ParamSpecifier::InOut); |
| VD->setInterfaceType(enumIfaceTy); |
| auto resultSlot = |
| F.begin()->createFunctionArgument(enumTI.getLoweredType(), VD); |
| dest = std::unique_ptr<Initialization>( |
| new KnownAddressInitialization(resultSlot)); |
| } |
| |
| Scope scope(Cleanups, CleanupLoc); |
| |
| // Emit the exploded constructor argument. |
| ArgumentSource payload; |
| if (element->hasAssociatedValues()) { |
| auto eltArgTy = element->getArgumentInterfaceType()->getCanonicalType(); |
| RValue arg = emitImplicitValueConstructorArg(*this, Loc, eltArgTy, element); |
| payload = ArgumentSource(Loc, std::move(arg)); |
| } |
| |
| // Emit the metatype argument. |
| emitConstructorMetatypeArg(*this, element); |
| |
| // If possible, emit the enum directly into the indirect return. |
| SGFContext C = (dest ? SGFContext(dest.get()) : SGFContext()); |
| ManagedValue mv = emitInjectEnum(Loc, std::move(payload), |
| enumTI.getLoweredType(), |
| element, C); |
| |
| // Return the enum. |
| auto ReturnLoc = ImplicitReturnLocation::getImplicitReturnLoc(Loc); |
| |
| if (dest) { |
| if (!mv.isInContext()) { |
| dest->copyOrInitValueInto(*this, Loc, mv, /*isInit*/ true); |
| dest->finishInitialization(*this); |
| } |
| scope.pop(); |
| B.createReturn(ReturnLoc, emitEmptyTuple(Loc)); |
| } else { |
| assert(enumTI.isLoadable() || !silConv.useLoweredAddresses()); |
| SILValue result = mv.ensurePlusOne(*this, ReturnLoc).forward(*this); |
| scope.pop(); |
| B.createReturn(ReturnLoc, result); |
| } |
| } |
| |
| bool Lowering::usesObjCAllocator(ClassDecl *theClass) { |
| // If the root class was implemented in Objective-C, use Objective-C's |
| // allocation methods because they may have been overridden. |
| return theClass->usesObjCObjectModel(); |
| } |
| |
| void SILGenFunction::emitClassConstructorAllocator(ConstructorDecl *ctor) { |
| assert(!ctor->isFactoryInit() && "factories should not be emitted here"); |
| |
| // Emit the prolog. Since we're just going to forward our args directly |
| // to the initializer, don't allocate local variables for them. |
| RegularLocation Loc(ctor); |
| Loc.markAutoGenerated(); |
| |
| // Forward the constructor arguments. |
| // FIXME: Handle 'self' along with the other body patterns. |
| SmallVector<SILValue, 8> args; |
| bindParametersForForwarding(ctor->getParameters(), args); |
| |
| SILValue selfMetaValue = emitConstructorMetatypeArg(*this, ctor); |
| |
| // Allocate the "self" value. |
| VarDecl *selfDecl = ctor->getImplicitSelfDecl(); |
| SILType selfTy = getLoweredType(selfDecl->getType()); |
| assert(selfTy.hasReferenceSemantics() && |
| "can't emit a value type ctor here"); |
| |
| // Use alloc_ref to allocate the object. |
| // TODO: allow custom allocation? |
| // FIXME: should have a cleanup in case of exception |
| auto selfClassDecl = ctor->getDeclContext()->getSelfClassDecl(); |
| |
| SILValue selfValue; |
| |
| // Allocate the 'self' value. |
| bool useObjCAllocation = usesObjCAllocator(selfClassDecl); |
| |
| if (ctor->hasClangNode() || |
| ctor->shouldUseObjCDispatch() || |
| ctor->isConvenienceInit()) { |
| assert(ctor->hasClangNode() || ctor->isObjC()); |
| // For an allocator thunk synthesized for an @objc convenience initializer |
| // or imported Objective-C init method, allocate using the metatype. |
| SILValue allocArg = selfMetaValue; |
| |
| // When using Objective-C allocation, convert the metatype |
| // argument to an Objective-C metatype. |
| if (useObjCAllocation) { |
| auto metaTy = allocArg->getType().castTo<MetatypeType>(); |
| metaTy = CanMetatypeType::get(metaTy.getInstanceType(), |
| MetatypeRepresentation::ObjC); |
| allocArg = B.createThickToObjCMetatype(Loc, allocArg, |
| getLoweredType(metaTy)); |
| } |
| |
| selfValue = B.createAllocRefDynamic(Loc, allocArg, selfTy, |
| useObjCAllocation, {}, {}); |
| } else { |
| assert(ctor->isDesignatedInit()); |
| // For a designated initializer, we know that the static type being |
| // allocated is the type of the class that defines the designated |
| // initializer. |
| F.setIsExactSelfClass(IsExactSelfClass); |
| selfValue = B.createAllocRef(Loc, selfTy, useObjCAllocation, false, |
| ArrayRef<SILType>(), ArrayRef<SILValue>()); |
| } |
| args.push_back(selfValue); |
| |
| // Call the initializer. Always use the Swift entry point, which will be a |
| // bridging thunk if we're calling ObjC. |
| auto initConstant = SILDeclRef(ctor, SILDeclRef::Kind::Initializer); |
| |
| ManagedValue initVal; |
| SILType initTy; |
| |
| // Call the initializer. |
| auto subMap = F.getForwardingSubstitutionMap(); |
| |
| std::tie(initVal, initTy) |
| = emitSiblingMethodRef(Loc, selfValue, initConstant, subMap); |
| |
| SILValue initedSelfValue = emitApplyWithRethrow(Loc, initVal.forward(*this), |
| initTy, subMap, args); |
| |
| emitProfilerIncrement(ctor->getTypecheckedBody()); |
| |
| // Return the initialized 'self'. |
| B.createReturn(ImplicitReturnLocation::getImplicitReturnLoc(Loc), |
| initedSelfValue); |
| } |
| |
| static void emitDefaultActorInitialization(SILGenFunction &SGF, |
| SILLocation loc, |
| ManagedValue self) { |
| auto &ctx = SGF.getASTContext(); |
| auto builtinName = ctx.getIdentifier( |
| getBuiltinName(BuiltinValueKind::InitializeDefaultActor)); |
| auto resultTy = SGF.SGM.Types.getEmptyTupleType(); |
| |
| FullExpr scope(SGF.Cleanups, CleanupLocation::get(loc)); |
| SGF.B.createBuiltin(loc, builtinName, resultTy, /*subs*/{}, |
| { self.borrow(SGF, loc).getValue() }); |
| } |
| |
| void SILGenFunction::emitClassConstructorInitializer(ConstructorDecl *ctor) { |
| MagicFunctionName = SILGenModule::getMagicFunctionName(ctor); |
| |
| assert(ctor->getTypecheckedBody() && "Class constructor without a body?"); |
| |
| // True if this constructor delegates to a peer constructor with self.init(). |
| bool isDelegating = false; |
| if (!ctor->hasStubImplementation()) { |
| isDelegating = ctor->getDelegatingOrChainedInitKind().initKind == |
| BodyInitKind::Delegating; |
| } |
| |
| // Set up the 'self' argument. If this class has a superclass, we set up |
| // self as a box. This allows "self reassignment" to happen in super init |
| // method chains, which is important for interoperating with Objective-C |
| // classes. We also use a box for delegating constructors, since the |
| // delegated-to initializer may also replace self. |
| // |
| // TODO: If we could require Objective-C classes to have an attribute to get |
| // this behavior, we could avoid runtime overhead here. |
| VarDecl *selfDecl = ctor->getImplicitSelfDecl(); |
| auto *dc = ctor->getDeclContext(); |
| auto selfClassDecl = dc->getSelfClassDecl(); |
| bool NeedsBoxForSelf = isDelegating || |
| (selfClassDecl->hasSuperclass() && !ctor->hasStubImplementation()); |
| bool usesObjCAllocator = Lowering::usesObjCAllocator(selfClassDecl); |
| |
| // If needed, mark 'self' as uninitialized so that DI knows to |
| // enforce its DI properties on stored properties. |
| MarkUninitializedInst::Kind MUKind; |
| |
| if (isDelegating) { |
| if (ctor->isObjC()) |
| MUKind = MarkUninitializedInst::DelegatingSelfAllocated; |
| else |
| MUKind = MarkUninitializedInst::DelegatingSelf; |
| } else if (selfClassDecl->requiresStoredPropertyInits() && |
| usesObjCAllocator) { |
| // Stored properties will be initialized in a separate |
| // .cxx_construct method called by the Objective-C runtime. |
| assert(selfClassDecl->hasSuperclass() && |
| "Cannot use ObjC allocation without a superclass"); |
| MUKind = MarkUninitializedInst::DerivedSelfOnly; |
| } else if (selfClassDecl->hasSuperclass()) |
| MUKind = MarkUninitializedInst::DerivedSelf; |
| else |
| MUKind = MarkUninitializedInst::RootSelf; |
| |
| if (NeedsBoxForSelf) { |
| // Allocate the local variable for 'self'. |
| emitLocalVariableWithCleanup(selfDecl, MUKind)->finishInitialization(*this); |
| } |
| |
| // Emit the prolog for the non-self arguments. |
| // FIXME: Handle self along with the other body patterns. |
| uint16_t ArgNo = emitProlog(ctor->getParameters(), /*selfParam=*/nullptr, |
| TupleType::getEmpty(F.getASTContext()), ctor, |
| ctor->hasThrows(), ctor->getThrowsLoc()); |
| |
| SILType selfTy = getLoweredLoadableType(selfDecl->getType()); |
| ManagedValue selfArg = B.createInputFunctionArgument(selfTy, selfDecl); |
| |
| if (!NeedsBoxForSelf) { |
| SILLocation PrologueLoc(selfDecl); |
| PrologueLoc.markAsPrologue(); |
| SILDebugVariable DbgVar(selfDecl->isLet(), ++ArgNo); |
| B.createDebugValue(PrologueLoc, selfArg.getValue(), DbgVar); |
| } |
| |
| // Initialize the default-actor instance. |
| if (selfClassDecl->isRootDefaultActor() && !isDelegating) { |
| SILLocation PrologueLoc(selfDecl); |
| PrologueLoc.markAsPrologue(); |
| emitDefaultActorInitialization(*this, PrologueLoc, selfArg); |
| } |
| |
| if (!ctor->hasStubImplementation()) { |
| assert(selfTy.hasReferenceSemantics() && |
| "can't emit a value type ctor here"); |
| if (NeedsBoxForSelf) { |
| SILLocation prologueLoc = RegularLocation(ctor); |
| prologueLoc.markAsPrologue(); |
| B.emitStoreValueOperation(prologueLoc, selfArg.forward(*this), |
| VarLocs[selfDecl].value, |
| StoreOwnershipQualifier::Init); |
| } else { |
| selfArg = B.createMarkUninitialized(selfDecl, selfArg, MUKind); |
| VarLocs[selfDecl] = VarLoc::get(selfArg.getValue()); |
| } |
| } |
| |
| // Prepare the end of initializer location. |
| SILLocation endOfInitLoc = RegularLocation(ctor); |
| endOfInitLoc.pointToEnd(); |
| |
| // Create a basic block to jump to for the implicit 'self' return. |
| // We won't emit the block until after we've emitted the body. |
| prepareEpilog(false, ctor->hasThrows(), |
| CleanupLocation::get(endOfInitLoc)); |
| |
| auto resultType = ctor->mapTypeIntoContext(ctor->getResultInterfaceType()); |
| |
| // If the constructor can fail, set up an alternative epilog for constructor |
| // failure. |
| SILBasicBlock *failureExitBB = nullptr; |
| SILArgument *failureExitArg = nullptr; |
| auto &resultLowering = getTypeLowering(resultType); |
| |
| if (ctor->isFailable()) { |
| SILBasicBlock *failureBB = createBasicBlock(FunctionSection::Postmatter); |
| |
| RegularLocation loc(ctor); |
| loc.markAutoGenerated(); |
| |
| // On failure, we'll clean up everything and return nil instead. |
| SILGenSavedInsertionPoint savedIP(*this, failureBB, |
| FunctionSection::Postmatter); |
| |
| failureExitBB = createBasicBlock(); |
| failureExitArg = failureExitBB->createPhiArgument( |
| resultLowering.getLoweredType(), OwnershipKind::Owned); |
| |
| Cleanups.emitCleanupsForReturn(ctor, IsForUnwind); |
| SILValue nilResult = |
| B.createEnum(loc, SILValue(), getASTContext().getOptionalNoneDecl(), |
| resultLowering.getLoweredType()); |
| B.createBranch(loc, failureExitBB, nilResult); |
| |
| B.setInsertionPoint(failureExitBB); |
| B.createReturn(loc, failureExitArg); |
| |
| FailDest = JumpDest(failureBB, Cleanups.getCleanupsDepth(), ctor); |
| } |
| |
| // Handle member initializers. |
| if (isDelegating) { |
| // A delegating initializer does not initialize instance |
| // variables. |
| } else if (ctor->hasStubImplementation()) { |
| // Nor does a stub implementation. |
| } else if (selfClassDecl->requiresStoredPropertyInits() && |
| usesObjCAllocator) { |
| // When the class requires all stored properties to have initial |
| // values and we're using Objective-C's allocation, stored |
| // properties are initialized via the .cxx_construct method, which |
| // will be called by the runtime. |
| |
| // Note that 'self' has been fully initialized at this point. |
| } else { |
| // Emit the member initializers. |
| emitMemberInitializers(ctor, selfDecl, selfClassDecl); |
| } |
| |
| emitProfilerIncrement(ctor->getTypecheckedBody()); |
| // Emit the constructor body. |
| emitStmt(ctor->getTypecheckedBody()); |
| |
| // Emit the call to super.init() right before exiting from the initializer. |
| if (NeedsBoxForSelf) { |
| if (auto *SI = ctor->getSuperInitCall()) { |
| B.setInsertionPoint(ReturnDest.getBlock()); |
| |
| emitRValue(SI); |
| |
| B.emitBlock(B.splitBlockForFallthrough(), ctor); |
| |
| ReturnDest = JumpDest(B.getInsertionBB(), |
| ReturnDest.getDepth(), |
| ReturnDest.getCleanupLocation()); |
| B.clearInsertionPoint(); |
| } |
| } |
| |
| CleanupStateRestorationScope SelfCleanupSave(Cleanups); |
| |
| // Build a custom epilog block, since the AST representation of the |
| // constructor decl (which has no self in the return type) doesn't match the |
| // SIL representation. |
| { |
| // Ensure that before we add additional cleanups, that we have emitted all |
| // cleanups at this point. |
| assert(!Cleanups.hasAnyActiveCleanups(getCleanupsDepth(), |
| ReturnDest.getDepth()) && |
| "emitting epilog in wrong scope"); |
| |
| SILGenSavedInsertionPoint savedIP(*this, ReturnDest.getBlock()); |
| auto cleanupLoc = CleanupLocation(ctor); |
| |
| // If we're using a box for self, reload the value at the end of the init |
| // method. |
| if (NeedsBoxForSelf) { |
| ManagedValue storedSelf = |
| ManagedValue::forUnmanaged(VarLocs[selfDecl].value); |
| selfArg = B.createLoadCopy(cleanupLoc, storedSelf); |
| } else { |
| // We have to do a retain because we are returning the pointer +1. |
| // |
| // SEMANTIC ARC TODO: When the verifier is complete, we will need to |
| // change this to selfArg = B.emitCopyValueOperation(...). Currently due |
| // to the way that SILGen performs folding of copy_value, destroy_value, |
| // the returned selfArg may be deleted causing us to have a |
| // dead-pointer. Instead just use the old self value since we have a |
| // class. |
| selfArg = B.createCopyValue(cleanupLoc, selfArg); |
| } |
| |
| // Inject the self value into an optional if the constructor is failable. |
| if (ctor->isFailable()) { |
| RegularLocation loc(ctor); |
| loc.markAutoGenerated(); |
| selfArg = B.createEnum(loc, selfArg, |
| getASTContext().getOptionalSomeDecl(), |
| getLoweredLoadableType(resultType)); |
| } |
| |
| // Save our cleanup state. We want all other potential cleanups to fire, but |
| // not this one. |
| if (selfArg.hasCleanup()) |
| SelfCleanupSave.pushCleanupState(selfArg.getCleanup(), |
| CleanupState::Dormant); |
| |
| // Translate our cleanup to the new top cleanup. |
| // |
| // This is needed to preserve the invariant in getEpilogBB that when |
| // cleanups are emitted, everything above ReturnDest.getDepth() has been |
| // emitted. This is not true if we use ManagedValue and friends in the |
| // epilogBB, thus the translation. We perform the same check above that |
| // getEpilogBB performs to ensure that we still do not have the same |
| // problem. |
| ReturnDest = std::move(ReturnDest).translate(getTopCleanup()); |
| } |
| |
| // Emit the epilog and post-matter. |
| auto returnLoc = emitEpilog(ctor, /*UsesCustomEpilog*/true); |
| |
| // Unpop our selfArg cleanup, so we can forward. |
| std::move(SelfCleanupSave).pop(); |
| |
| // Finish off the epilog by returning. If this is a failable ctor, then we |
| // actually jump to the failure epilog to keep the invariant that there is |
| // only one SIL return instruction per SIL function. |
| if (B.hasValidInsertionPoint()) { |
| if (failureExitBB) |
| B.createBranch(returnLoc, failureExitBB, selfArg.forward(*this)); |
| else |
| B.createReturn(returnLoc, selfArg.forward(*this)); |
| } |
| } |
| |
| static ManagedValue emitSelfForMemberInit(SILGenFunction &SGF, SILLocation loc, |
| VarDecl *selfDecl) { |
| CanType selfFormalType = selfDecl->getType()->getCanonicalType(); |
| if (selfFormalType->hasReferenceSemantics()) |
| return SGF.emitRValueForDecl(loc, selfDecl, selfFormalType, |
| AccessSemantics::DirectToStorage, |
| SGFContext::AllowImmediatePlusZero) |
| .getAsSingleValue(SGF, loc); |
| else |
| return SGF.emitAddressOfLocalVarDecl(loc, selfDecl, selfFormalType, |
| SGFAccessKind::Write); |
| } |
| |
| // FIXME: Can emitMemberInit() share code with InitializationForPattern in |
| // SILGenDecl.cpp? Note that this version operates on stored properties of |
| // types, whereas the former only knows how to handle local bindings, but |
| // we could generalize it. |
| static InitializationPtr |
| emitMemberInit(SILGenFunction &SGF, VarDecl *selfDecl, Pattern *pattern) { |
| switch (pattern->getKind()) { |
| case PatternKind::Paren: |
| return emitMemberInit(SGF, selfDecl, |
| cast<ParenPattern>(pattern)->getSubPattern()); |
| |
| case PatternKind::Tuple: { |
| TupleInitialization *init = new TupleInitialization(); |
| auto tuple = cast<TuplePattern>(pattern); |
| for (auto &elt : tuple->getElements()) { |
| init->SubInitializations.push_back( |
| emitMemberInit(SGF, selfDecl, elt.getPattern())); |
| } |
| return InitializationPtr(init); |
| } |
| |
| case PatternKind::Named: { |
| auto named = cast<NamedPattern>(pattern); |
| |
| auto self = emitSelfForMemberInit(SGF, pattern, selfDecl); |
| |
| auto *field = named->getDecl(); |
| |
| auto selfTy = self.getType(); |
| auto fieldTy = |
| selfTy.getFieldType(field, SGF.SGM.M, SGF.getTypeExpansionContext()); |
| SILValue slot; |
| |
| if (auto *structDecl = dyn_cast<StructDecl>(field->getDeclContext())) { |
| slot = SGF.B.createStructElementAddr(pattern, self.forward(SGF), field, |
| fieldTy.getAddressType()); |
| } else { |
| assert(isa<ClassDecl>(field->getDeclContext())); |
| slot = SGF.B.createRefElementAddr(pattern, self.forward(SGF), field, |
| fieldTy.getAddressType()); |
| } |
| |
| return InitializationPtr(new KnownAddressInitialization(slot)); |
| } |
| |
| case PatternKind::Any: |
| return InitializationPtr(new BlackHoleInitialization());; |
| |
| case PatternKind::Typed: |
| return emitMemberInit(SGF, selfDecl, |
| cast<TypedPattern>(pattern)->getSubPattern()); |
| |
| case PatternKind::Binding: |
| return emitMemberInit(SGF, selfDecl, |
| cast<BindingPattern>(pattern)->getSubPattern()); |
| |
| #define PATTERN(Name, Parent) |
| #define REFUTABLE_PATTERN(Name, Parent) case PatternKind::Name: |
| #include "swift/AST/PatternNodes.def" |
| llvm_unreachable("Refutable pattern in stored property pattern binding"); |
| } |
| } |
| |
| static std::pair<AbstractionPattern, CanType> |
| getInitializationTypeInContext( |
| DeclContext *fromDC, DeclContext *toDC, |
| Pattern *pattern) { |
| auto interfaceType = pattern->getType()->mapTypeOutOfContext(); |
| |
| // If this pattern is initializing the backing storage for a property |
| // with an attached wrapper that is initialized with `=`, the |
| // initialization type is the original property type. |
| if (auto singleVar = pattern->getSingleVar()) { |
| if (auto originalProperty = singleVar->getOriginalWrappedProperty()) { |
| if (originalProperty->isPropertyMemberwiseInitializedWithWrappedType()) |
| interfaceType = originalProperty->getPropertyWrapperInitValueInterfaceType(); |
| } |
| } |
| |
| AbstractionPattern origType( |
| fromDC->getGenericSignatureOfContext().getCanonicalSignature(), |
| interfaceType->getCanonicalType()); |
| |
| auto substType = toDC->mapTypeIntoContext(interfaceType)->getCanonicalType(); |
| |
| return std::make_pair(origType, substType); |
| } |
| |
| static void |
| emitAndStoreInitialValueInto(SILGenFunction &SGF, |
| SILLocation loc, |
| PatternBindingDecl *pbd, unsigned i, |
| SubstitutionMap subs, |
| AbstractionPattern origType, |
| CanType substType, |
| Initialization *init) { |
| bool injectIntoWrapper = false; |
| if (auto singleVar = pbd->getSingleVar()) { |
| auto originalVar = singleVar->getOriginalWrappedProperty(); |
| if (originalVar && |
| originalVar->isPropertyMemberwiseInitializedWithWrappedType()) { |
| injectIntoWrapper = true; |
| } |
| } |
| |
| SGFContext C = (injectIntoWrapper ? SGFContext() : SGFContext(init)); |
| |
| RValue result = SGF.emitApplyOfStoredPropertyInitializer( |
| pbd->getExecutableInit(i), |
| pbd->getAnchoringVarDecl(i), |
| subs, substType, origType, C); |
| |
| // need to store result into the init if its in context |
| |
| // If we have the backing storage for a property with an attached |
| // property wrapper initialized with `=`, inject the value into an |
| // instance of the wrapper. |
| if (injectIntoWrapper) { |
| auto *singleVar = pbd->getSingleVar(); |
| result = maybeEmitPropertyWrapperInitFromValue( |
| SGF, pbd->getExecutableInit(i), |
| singleVar, subs, std::move(result)); |
| } |
| |
| if (!result.isInContext()) |
| std::move(result).forwardInto(SGF, loc, init); |
| } |
| |
| void SILGenFunction::emitMemberInitializers(DeclContext *dc, |
| VarDecl *selfDecl, |
| NominalTypeDecl *nominal) { |
| auto subs = getSubstitutionsForPropertyInitializer(dc, nominal); |
| |
| for (auto member : nominal->getMembers()) { |
| // Find instance pattern binding declarations that have initializers. |
| if (auto pbd = dyn_cast<PatternBindingDecl>(member)) { |
| if (pbd->isStatic()) continue; |
| |
| for (auto i : range(pbd->getNumPatternEntries())) { |
| auto init = pbd->getExecutableInit(i); |
| if (!init) continue; |
| |
| auto *varPattern = pbd->getPattern(i); |
| |
| // Cleanup after this initialization. |
| FullExpr scope(Cleanups, varPattern); |
| |
| // Get the type of the initialization result, in terms |
| // of the constructor context's archetypes. |
| auto resultType = getInitializationTypeInContext( |
| pbd->getDeclContext(), dc, varPattern); |
| AbstractionPattern origType = resultType.first; |
| CanType substType = resultType.second; |
| |
| // Figure out what we're initializing. |
| auto memberInit = emitMemberInit(*this, selfDecl, varPattern); |
| |
| // This whole conversion thing is about eliminating the |
| // paired orig-to-subst subst-to-orig conversions that |
| // will happen if the storage is at a different abstraction |
| // level than the constructor. When emitApply() is used |
| // to call the stored property initializer, it naturally |
| // wants to convert the result back to the most substituted |
| // abstraction level. To undo this, we use a converting |
| // initialization and rely on the peephole that optimizes |
| // out the redundant conversion. |
| auto loweredResultTy = getLoweredType(origType, substType); |
| auto loweredSubstTy = getLoweredType(substType); |
| |
| if (loweredResultTy != loweredSubstTy) { |
| Conversion conversion = Conversion::getSubstToOrig( |
| origType, substType, |
| loweredResultTy); |
| |
| ConvertingInitialization convertingInit(conversion, |
| SGFContext(memberInit.get())); |
| |
| emitAndStoreInitialValueInto(*this, varPattern, pbd, i, subs, |
| origType, substType, &convertingInit); |
| |
| auto finalValue = convertingInit.finishEmission( |
| *this, varPattern, ManagedValue::forInContext()); |
| if (!finalValue.isInContext()) |
| finalValue.forwardInto(*this, varPattern, memberInit.get()); |
| } else { |
| emitAndStoreInitialValueInto(*this, varPattern, pbd, i, subs, |
| origType, substType, memberInit.get()); |
| } |
| } |
| } |
| } |
| } |
| |
| void SILGenFunction::emitIVarInitializer(SILDeclRef ivarInitializer) { |
| auto cd = cast<ClassDecl>(ivarInitializer.getDecl()); |
| RegularLocation loc(cd); |
| loc.markAutoGenerated(); |
| |
| // Emit 'self', then mark it uninitialized. |
| auto selfDecl = cd->getDestructor()->getImplicitSelfDecl(); |
| SILType selfTy = getLoweredLoadableType(selfDecl->getType()); |
| SILValue selfArg = F.begin()->createFunctionArgument(selfTy, selfDecl); |
| SILLocation PrologueLoc(selfDecl); |
| PrologueLoc.markAsPrologue(); |
| // Hard-code self as argument number 1. |
| SILDebugVariable DbgVar(selfDecl->isLet(), 1); |
| B.createDebugValue(PrologueLoc, selfArg, DbgVar); |
| selfArg = B.createMarkUninitialized(selfDecl, selfArg, |
| MarkUninitializedInst::RootSelf); |
| assert(selfTy.hasReferenceSemantics() && "can't emit a value type ctor here"); |
| VarLocs[selfDecl] = VarLoc::get(selfArg); |
| |
| auto cleanupLoc = CleanupLocation::get(loc); |
| prepareEpilog(false, false, cleanupLoc); |
| |
| // Emit the initializers. |
| emitMemberInitializers(cd, selfDecl, cd); |
| |
| // Return 'self'. |
| B.createReturn(loc, selfArg); |
| |
| emitEpilog(loc); |
| } |