From 1e28b0a3122fb2675eaf4e190a0da7c9cc3e581a Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Wed, 8 Oct 2025 17:34:09 -0700 Subject: [PATCH] SILGen: revise emission of UnreachableExpr The prior emission strategy produced some scary SIL where we have a copy of uninitialized memory: ``` ignored_use %14 %16 = alloc_stack $T unreachable bb1: copy_addr [take] %16 to [init] %0 dealloc_stack %16 ``` I assume this was done to dodge the SILVerifier, which will catch a take of an uninitialized address, had the alloc_stack been in any reachable predecessor. We already have a representation for an undefined value in SIL, so I'd rather use that than try to sneak one past the verifier. This adjusted emission also is opaque values friendly, as it doesn't assume the result value is an address. resolves rdar://162239557 --- lib/SILGen/SILGenExpr.cpp | 28 ++++++++++++++++------- test/SILGen/unreachable_expr.swift | 20 ++++++++++++++++ test/SILGen/variadic-generic-tuples.swift | 10 ++++++++ 3 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 test/SILGen/unreachable_expr.swift diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 5b148e7dc6952..db3ab3ef6969c 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -2606,19 +2606,31 @@ RValue RValueEmitter::visitUnderlyingToOpaqueExpr(UnderlyingToOpaqueExpr *E, } RValue RValueEmitter::visitUnreachableExpr(UnreachableExpr *E, SGFContext C) { - // Emit the expression, followed by an unreachable. To produce a value of - // arbitrary type, we emit a temporary allocation, with the use of the - // allocation in the unreachable block. The SILOptimizer will eliminate both - // the unreachable block and unused allocation. + // Emit the expression, followed by an unreachable. SGF.emitIgnoredExpr(E->getSubExpr()); + SGF.B.createUnreachable(E); + + // Continue code generation in a block with no predecessors. + // Whatever code is emitted here is guaranteed to be removed by SIL passes. + SGF.B.emitBlock(SGF.createBasicBlock()); + // Since the type is uninhabited, use a SILUndef of so that we can return + // some sort of RValue from this API. auto &lowering = SGF.getTypeLowering(E->getType()); - auto resultAddr = SGF.emitTemporaryAllocation(E, lowering.getLoweredType()); + auto loweredTy = lowering.getLoweredType(); + auto undef = SILUndef::get(SGF.F, loweredTy); - SGF.B.createUnreachable(E); - SGF.B.emitBlock(SGF.createBasicBlock()); + // Create an alloc initialized with contents from the undefined addr type. + // It seems pack addresses do not satisfy isPlusOneOrTrivial, so we need an + // actual allocation. + if (loweredTy.isAddress()) { + auto resultAddr = SGF.emitTemporaryAllocation(E, loweredTy); + SGF.emitSemanticStore(E, undef, resultAddr, lowering, IsInitialization); + return RValue(SGF, E, SGF.emitManagedRValueWithCleanup(resultAddr)); + } - return RValue(SGF, E, SGF.emitManagedRValueWithCleanup(resultAddr)); + // Otherwise, if it's not an address, just emit the undef value itself. + return RValue(SGF, E, ManagedValue::forRValueWithoutOwnership(undef)); } VarargsInfo Lowering::emitBeginVarargs(SILGenFunction &SGF, SILLocation loc, diff --git a/test/SILGen/unreachable_expr.swift b/test/SILGen/unreachable_expr.swift new file mode 100644 index 0000000000000..9812714c004a6 --- /dev/null +++ b/test/SILGen/unreachable_expr.swift @@ -0,0 +1,20 @@ +// RUN: %target-swift-emit-silgen %s -verify -sil-verify-all | %FileCheck %s --check-prefixes CHECK,REG +// RUN: %target-swift-emit-silgen %s -verify -sil-verify-all -enable-sil-opaque-values | %FileCheck %s --check-prefixes CHECK,OV + +// CHECK-LABEL: sil{{.*}} [ossa] @{{.*}}uninhabited_generic{{.*}} +// CHECK: [[NEVER:%[^,]+]] = apply {{.*}} -> Never +// CHECK-NEXT: ignored_use [[NEVER]] +// CHECK-NEXT: unreachable +// +// CHECK: bb1: +// CHECK-NOT: Preds + + // Without opaque values, take from an undef address, + // through a temporary alloc, to eventually the out-parameter %0 +// REG-NEXT: [[BOGUS_ALLOC:%.*]] = alloc_stack $T +// REG-NEXT: copy_addr [take] undef to [init] [[BOGUS_ALLOC]] +// REG-NEXT: copy_addr [take] [[BOGUS_ALLOC]] to [init] %0 + + // With opaque values, simply return the undef value +// OV-NEXT: return undef : $T +func uninhabited_generic() -> T { fatalError("todo") } diff --git a/test/SILGen/variadic-generic-tuples.swift b/test/SILGen/variadic-generic-tuples.swift index 007253c2be790..9bcf744c5e5cf 100644 --- a/test/SILGen/variadic-generic-tuples.swift +++ b/test/SILGen/variadic-generic-tuples.swift @@ -430,6 +430,16 @@ func convertVoidPayloads() { convertPayloads(as: Void.self) } +// CHECK-LABEL: sil{{.*}} [ossa] @{{.*}}convertPayloads{{.*}} : +// CHECK: ignored_use {{.*}} +// CHECK-NEXT: unreachable +// +// CHECK: bb1: +// CHECK-NOT: Preds +// CHECK: [[BOGUS_ALLOC:%.*]] = alloc_stack $(repeat each Value) +// CHECK-NEXT: copy_addr [take] undef to [init] [[BOGUS_ALLOC]] +// +// CHECK: = tuple_pack_element_addr {{.*}} of [[BOGUS_ALLOC]] func convertPayloads(as valueTypes: repeat (each Value).Type) -> (repeat each Value) { fatalError() }