From c23434a569d13fa0dcbd797170a5cd14a0a91c4c Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Thu, 21 Aug 2014 18:35:33 +0000 Subject: [PATCH] SILGen: Wrap the result of a failable value constructor in an optional. Swift SVN r21370 --- lib/SILGen/SILGenExpr.cpp | 51 +++++- lib/SILGen/SILGenStmt.cpp | 4 +- test/SILGen/failable_initializers.swift | 214 ++++++++++++++++++++++++ 3 files changed, 266 insertions(+), 3 deletions(-) create mode 100644 test/SILGen/failable_initializers.swift diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 2b213e25498..485b558f9d7 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -3321,7 +3321,7 @@ void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) { // Emit the prolog. emitProlog(ctor->getBodyParamPatterns()[1], - ctor->getImplicitSelfDecl()->getType()->getInOutObjectType(), + ctor->getResultType(), ctor); emitConstructorMetatypeArg(*this, ctor); @@ -3356,10 +3356,42 @@ void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) { if (lowering.isAddressOnly()) { assert(IndirectReturnAddress && "no indirect return for address-only ctor?!"); + + // Get the address to which to store the result. + SILValue returnAddress; + switch (ctor->getFailability()) { + // For non-failable initializers, store to the return address directly. + case OTK_None: + returnAddress = IndirectReturnAddress; + break; + // If this is a failable initializer, project out the payload. + case OTK_Optional: + case OTK_ImplicitlyUnwrappedOptional: + returnAddress = B.createInitEnumDataAddr(ctor, IndirectReturnAddress, + getASTContext().getOptionalSomeDecl(ctor->getFailability()), + selfLV.getType()); + break; + } + // We have to do a non-take copy because someone else may be using the box. - B.createCopyAddr(cleanupLoc, selfLV, IndirectReturnAddress, + B.createCopyAddr(cleanupLoc, selfLV, returnAddress, IsNotTake, IsInitialization); B.emitStrongRelease(cleanupLoc, selfBox); + + // Inject the enum tag if the result is optional because of failability. + switch (ctor->getFailability()) { + case OTK_None: + // Not optional. + break; + + case OTK_Optional: + case OTK_ImplicitlyUnwrappedOptional: + // Inject the 'Some' tag. + B.createInjectEnumAddr(ctor, IndirectReturnAddress, + getASTContext().getOptionalSomeDecl(ctor->getFailability())); + break; + } + B.createReturn(returnLoc, emitEmptyTuple(ctor)); return; } @@ -3372,6 +3404,21 @@ void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) { // Release the box. B.emitStrongRelease(cleanupLoc, selfBox); + + // Inject the self value into an optional if the constructor is failable. + switch (ctor->getFailability()) { + case OTK_None: + // Not optional. + break; + + case OTK_Optional: + case OTK_ImplicitlyUnwrappedOptional: + selfValue = B.createEnum(ctor, selfValue, + getASTContext().getOptionalSomeDecl(ctor->getFailability()), + getLoweredLoadableType(ctor->getResultType())); + break; + } + B.createReturn(returnLoc, selfValue); } diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index d625f8cbc85..8bf3044925f 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -627,7 +627,9 @@ void SILGenFunction::visitFallthroughStmt(FallthroughStmt *S) { } void SILGenFunction::visitFailStmt(FailStmt *S) { - llvm_unreachable("fail statement in SILGen"); + + // TODO + B.createUnreachable(S); } void SILGenModule::visitIfConfigDecl(IfConfigDecl *ICD) { diff --git a/test/SILGen/failable_initializers.swift b/test/SILGen/failable_initializers.swift new file mode 100644 index 00000000000..8b39af50cce --- /dev/null +++ b/test/SILGen/failable_initializers.swift @@ -0,0 +1,214 @@ +// RUN: %swift -emit-silgen %s | FileCheck %s + +protocol P {} +class C: P {} + +struct LoadableStruct { + var x: C + + // CHECK-LABEL: sil @_TFV21failable_initializers14LoadableStructCfMS0_FT3optSb_GSqS0__ + init?(opt: Bool) { + // CHECK: [[SELF_BOX:%.*]] = alloc_box $LoadableStruct + // CHECK: [[SELF_MARKED:%.*]] = mark_uninitialized [rootself] [[SELF_BOX]]#1 + x = C() + // TODO: failure + // CHECK: bb{{.*}}: + // CHECK: unreachable + if opt { + return nil + } + + // CHECK: bb{{.*}}: + // CHECK: [[SELF:%.*]] = load [[SELF_MARKED]] + // CHECK: [[SELF_OPT:%.*]] = enum $Optional, #Optional.Some!enumelt.1, [[SELF]] + // CHECK: return [[SELF_OPT]] + } + + // CHECK-LABEL: sil @_TFV21failable_initializers14LoadableStructCfMS0_FT3iuoSb_GSQS0__ + init!(iuo: Bool) { + // CHECK: [[SELF_BOX:%.*]] = alloc_box $LoadableStruct + // CHECK: [[SELF_MARKED:%.*]] = mark_uninitialized [rootself] [[SELF_BOX]]#1 + x = C() + // TODO: failure + // CHECK: bb{{.*}}: + // CHECK: unreachable + if iuo { + return nil + } + + // CHECK: bb{{.*}}: + // CHECK: [[SELF:%.*]] = load [[SELF_MARKED]] + // CHECK: [[SELF_OPT:%.*]] = enum $ImplicitlyUnwrappedOptional, #ImplicitlyUnwrappedOptional.Some!enumelt.1, [[SELF]] + // CHECK: return [[SELF_OPT]] + } + + /* + init?(delegatesOptOpt: Bool) { + self.init(opt: true) + } + + init!(delegatesIUOIUO: Bool) { + self.init(iuo: true) + } + + init?(delegatesOptIUO: Bool) { + self.init(iuo: true) + } + + init!(delegatesIUOOpt: Bool) { + self.init(opt: true) + } + + init(delegatesNormIUO: Bool) { + self.init(iuo: true) + } + */ +} + +struct AddressOnlyStruct { + var x: P + + // CHECK-LABEL: sil @_TFV21failable_initializers17AddressOnlyStructCfMS0_FT3optSb_GSqS0__ + init?(opt: Bool) { + // CHECK: [[SELF_BOX:%.*]] = alloc_box $AddressOnlyStruct + // CHECK: [[SELF_MARKED:%.*]] = mark_uninitialized [rootself] [[SELF_BOX]]#1 + x = C() + // TODO: failure + // CHECK: bb{{.*}}: + // CHECK: unreachable + if opt { + return nil + } + + // CHECK: bb{{.*}}: + // CHECK: [[DEST_PAYLOAD:%.*]] = init_enum_data_addr %0 : $*Optional, #Optional.Some + // CHECK: copy_addr [[SELF_MARKED]] to [initialization] [[DEST_PAYLOAD]] + // CHECK: inject_enum_addr %0 : $*Optional, #Optional.Some + } + + // CHECK-LABEL: sil @_TFV21failable_initializers17AddressOnlyStructCfMS0_FT3iuoSb_GSQS0__ + init!(iuo: Bool) { + // CHECK: [[SELF_BOX:%.*]] = alloc_box $AddressOnlyStruct + // CHECK: [[SELF_MARKED:%.*]] = mark_uninitialized [rootself] [[SELF_BOX]]#1 + x = C() + // TODO: failure + // CHECK: bb{{.*}}: + // CHECK: unreachable + if iuo { + return nil + } + + // CHECK: bb{{.*}}: + // CHECK: [[DEST_PAYLOAD:%.*]] = init_enum_data_addr %0 : $*ImplicitlyUnwrappedOptional, #ImplicitlyUnwrappedOptional.Some + // CHECK: copy_addr [[SELF_MARKED]] to [initialization] [[DEST_PAYLOAD]] + // CHECK: inject_enum_addr %0 : $*ImplicitlyUnwrappedOptional, #ImplicitlyUnwrappedOptional.Some + } + + /* + init?(delegatesOptOpt: Bool) { + self.init(opt: true) + } + + init!(delegatesIUOIUO: Bool) { + self.init(iuo: true) + } + + init?(delegatesOptIUO: Bool) { + self.init(iuo: true) + } + + init!(delegatesIUOOpt: Bool) { + self.init(opt: true) + } + + init(delegatesNormIUO: Bool) { + self.init(iuo: true) + } + */ +} + +enum LoadableEnum { + case A(C) + + init?(opt: Bool) { + self = A(C()) + + if opt { + return nil + } + } + + init!(iuo: Bool) { + self = A(C()) + + if iuo { + return nil + } + } + + /* + init?(delegatesOptOpt: Bool) { + self.init(opt: true) + } + + init!(delegatesIUOIUO: Bool) { + self.init(iuo: true) + } + + init?(delegatesOptIUO: Bool) { + self.init(iuo: true) + } + + init!(delegatesIUOOpt: Bool) { + self.init(opt: true) + } + + init(delegatesNormIUO: Bool) { + self.init(iuo: true) + } + */ +} + +enum AddressOnlyEnum { + case A(P) + + init?(opt: Bool) { + self = A(C()) + + if opt { + return nil + } + } + + init!(iuo: Bool) { + self = A(C()) + + if iuo { + return nil + } + } + + /* + init?(delegatesOptOpt: Bool) { + self.init(opt: true) + } + + init!(delegatesIUOIUO: Bool) { + self.init(iuo: true) + } + + init?(delegatesOptIUO: Bool) { + self.init(iuo: true) + } + + init!(delegatesIUOOpt: Bool) { + self.init(opt: true) + } + + init(delegatesNormIUO: Bool) { + self.init(iuo: true) + } + */ +} + +