Skip to content

Commit bb2fc82

Browse files
authored
Merge pull request #65060 from eeckstein/drop_deinit
Add the `drop_deinit` instruction
2 parents 0bdacfb + 6b44d5a commit bb2fc82

32 files changed

+230
-16
lines changed

SwiftCompilerSources/Sources/SIL/Instruction.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,10 @@ final public class MoveValueInst : SingleValueInstruction, UnaryInstruction {
647647
public var fromValue: Value { operand.value }
648648
}
649649

650+
final public class DropDeinitInst : SingleValueInstruction, UnaryInstruction {
651+
public var fromValue: Value { operand.value }
652+
}
653+
650654
final public class StrongCopyUnownedValueInst : SingleValueInstruction, UnaryInstruction {}
651655

652656
final public class StrongCopyUnmanagedValueInst : SingleValueInstruction, UnaryInstruction {}

SwiftCompilerSources/Sources/SIL/Registration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ public func registerSILClasses() {
125125
register(ProjectBoxInst.self)
126126
register(CopyValueInst.self)
127127
register(MoveValueInst.self)
128+
register(DropDeinitInst.self)
128129
register(EndCOWMutationInst.self)
129130
register(ClassifyBridgeObjectInst.self)
130131
register(PartialApplyInst.self)

docs/SIL.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6117,6 +6117,29 @@ a type `T` into the move only value space.
61176117
The ``lexical`` attribute specifies that the value corresponds to a local
61186118
variable in the Swift source.
61196119

6120+
6121+
drop_deinit
6122+
```````````
6123+
6124+
::
6125+
6126+
sil-instruction ::= 'drop_deinit' sil-operand
6127+
6128+
%1 = drop_deinit %0 : $T
6129+
// T must be a move-only type
6130+
// %1 is an @owned T
6131+
%3 = drop_deinit %2 : $*T
6132+
// T must be a move-only type
6133+
// %2 has type *T
6134+
6135+
This instruction is a marker for a following destroy instruction to suppress
6136+
the call of the move-only type's deinitializer.
6137+
The instruction accepts an object or address type.
6138+
If its argument is an object type it takes in an `@owned T` and produces a new
6139+
`@owned T`. If its argument is an address type, it's an identity projection.
6140+
6141+
The instruction is only valid in ownership SIL.
6142+
61206143
release_value
61216144
`````````````
61226145

include/swift/SIL/MemAccessUtils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1632,6 +1632,7 @@ inline bool isAccessStorageIdentityCast(SingleValueInstruction *svi) {
16321632
// Simply pass-thru the incoming address.
16331633
case SILInstructionKind::MarkUninitializedInst:
16341634
case SILInstructionKind::MarkMustCheckInst:
1635+
case SILInstructionKind::DropDeinitInst:
16351636
case SILInstructionKind::MarkUnresolvedReferenceBindingInst:
16361637
case SILInstructionKind::MarkDependenceInst:
16371638
case SILInstructionKind::CopyValueInst:

include/swift/SIL/SILBuilder.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1355,6 +1355,14 @@ class SILBuilder {
13551355
operand, isLexical));
13561356
}
13571357

1358+
DropDeinitInst *createDropDeinit(SILLocation loc, SILValue operand) {
1359+
assert(getFunction().hasOwnership());
1360+
assert(!operand->getType().isTrivial(getFunction()) &&
1361+
"Should not be passing trivial values to this api.");
1362+
return insert(new (getModule()) DropDeinitInst(getSILDebugLocation(loc),
1363+
operand));
1364+
}
1365+
13581366
MarkUnresolvedMoveAddrInst *createMarkUnresolvedMoveAddr(SILLocation loc,
13591367
SILValue srcAddr,
13601368
SILValue takeAddr) {

include/swift/SIL/SILCloner.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1888,6 +1888,17 @@ void SILCloner<ImplClass>::visitMoveValueInst(MoveValueInst *Inst) {
18881888
recordClonedInstruction(Inst, MVI);
18891889
}
18901890

1891+
template <typename ImplClass>
1892+
void SILCloner<ImplClass>::visitDropDeinitInst(DropDeinitInst *Inst) {
1893+
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
1894+
if (!getBuilder().hasOwnership()) {
1895+
return recordFoldedValue(Inst, getOpValue(Inst->getOperand()));
1896+
}
1897+
auto *MVI = getBuilder().createDropDeinit(getOpLocation(Inst->getLoc()),
1898+
getOpValue(Inst->getOperand()));
1899+
recordClonedInstruction(Inst, MVI);
1900+
}
1901+
18911902
template <typename ImplClass>
18921903
void SILCloner<ImplClass>::visitMarkMustCheckInst(MarkMustCheckInst *Inst) {
18931904
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));

include/swift/SIL/SILInstruction.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8268,6 +8268,15 @@ class MoveValueInst
82688268
void removeIsLexical() { lexical = false; }
82698269
};
82708270

8271+
class DropDeinitInst
8272+
: public UnaryInstructionBase<SILInstructionKind::DropDeinitInst,
8273+
SingleValueInstruction> {
8274+
friend class SILBuilder;
8275+
8276+
DropDeinitInst(SILDebugLocation DebugLoc, SILValue operand)
8277+
: UnaryInstructionBase(DebugLoc, operand, operand->getType()) {}
8278+
};
8279+
82718280
/// Equivalent to a copy_addr to [init] except that it is used for diagnostics
82728281
/// and should not be pattern matched. During the diagnostic passes, the "move
82738282
/// function" checker for addresses always converts this to a copy_addr [init]

include/swift/SIL/SILNodes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,8 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction)
461461
// effects relative to other OSSA values like copy_value.
462462
SINGLE_VALUE_INST(MoveValueInst, move_value, SingleValueInstruction, None,
463463
DoesNotRelease)
464+
SINGLE_VALUE_INST(DropDeinitInst, drop_deinit, SingleValueInstruction, None,
465+
DoesNotRelease)
464466
// A canary value inserted by a SIL generating frontend to signal to the move
465467
// checker to check a specific value. Valid only in Raw SIL. The relevant
466468
// checkers should remove the mark_must_check instruction after successfully

lib/IRGen/IRGenSIL.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,6 +1222,10 @@ class IRGenSILFunction :
12221222
auto e = getLoweredExplosion(i->getOperand());
12231223
setLoweredExplosion(i, e);
12241224
}
1225+
void visitDropDeinitInst(DropDeinitInst *i) {
1226+
auto e = getLoweredExplosion(i->getOperand());
1227+
setLoweredExplosion(i, e);
1228+
}
12251229
void visitMarkMustCheckInst(MarkMustCheckInst *i) {
12261230
llvm_unreachable("Invalid in Lowered SIL");
12271231
}

lib/SIL/IR/OperandOwnership.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,12 @@ OperandOwnershipClassifier::visitStoreBorrowInst(StoreBorrowInst *i) {
455455
return OperandOwnership::TrivialUse;
456456
}
457457

458+
OperandOwnership
459+
OperandOwnershipClassifier::visitDropDeinitInst(DropDeinitInst *i) {
460+
return i->getType().isAddress() ? OperandOwnership::TrivialUse
461+
: OperandOwnership::ForwardingConsume;
462+
}
463+
458464
// Get the OperandOwnership for instantaneous apply, yield, and return uses.
459465
// This does not apply to uses that begin an explicit borrow scope in the
460466
// caller, such as begin_apply.

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2385,7 +2385,7 @@ struct DeallocatorConventions : Conventions {
23852385

23862386
ParameterConvention
23872387
getIndirectSelfParameter(const AbstractionPattern &type) const override {
2388-
llvm_unreachable("Deallocators do not have indirect self parameters");
2388+
return ParameterConvention::Indirect_In;
23892389
}
23902390

23912391
static bool classof(const Conventions *C) {

lib/SIL/IR/SILPrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1996,6 +1996,10 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
19961996
*this << getIDAndType(I->getOperand());
19971997
}
19981998

1999+
void visitDropDeinitInst(DropDeinitInst *I) {
2000+
*this << getIDAndType(I->getOperand());
2001+
}
2002+
19992003
void visitMarkMustCheckInst(MarkMustCheckInst *I) {
20002004
using CheckKind = MarkMustCheckInst::CheckKind;
20012005
switch (I->getCheckKind()) {

lib/SIL/IR/ValueOwnership.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,10 @@ ValueOwnershipKind ValueOwnershipKindClassifier::visitLoadInst(LoadInst *LI) {
354354
llvm_unreachable("Unhandled LoadOwnershipQualifier in switch.");
355355
}
356356

357+
ValueOwnershipKind ValueOwnershipKindClassifier::visitDropDeinitInst(DropDeinitInst *ddi) {
358+
return ddi->getType().isAddress() ? OwnershipKind::None : OwnershipKind::Owned;
359+
}
360+
357361
ValueOwnershipKind
358362
ValueOwnershipKindClassifier::visitPartialApplyInst(PartialApplyInst *PA) {
359363
// partial_apply instructions are modeled as creating an owned value during

lib/SIL/Parser/ParseSIL.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3698,6 +3698,15 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B,
36983698
break;
36993699
}
37003700

3701+
case SILInstructionKind::DropDeinitInst: {
3702+
if (parseTypedValueRef(Val, B))
3703+
return true;
3704+
if (parseSILDebugLocation(InstLoc, B))
3705+
return true;
3706+
ResultVal = B.createDropDeinit(InstLoc, Val);
3707+
break;
3708+
}
3709+
37013710
case SILInstructionKind::MarkMustCheckInst: {
37023711
StringRef AttrName;
37033712
if (!parseSILOptional(AttrName, *this)) {

lib/SIL/Utils/InstructionUtils.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,7 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType)
441441
case SILInstructionKind::ValueToBridgeObjectInst:
442442
case SILInstructionKind::MarkDependenceInst:
443443
case SILInstructionKind::MoveValueInst:
444+
case SILInstructionKind::DropDeinitInst:
444445
case SILInstructionKind::MarkMustCheckInst:
445446
case SILInstructionKind::MarkUnresolvedReferenceBindingInst:
446447
case SILInstructionKind::CopyableToMoveOnlyWrapperValueInst:

lib/SIL/Utils/MemAccessUtils.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1864,6 +1864,7 @@ AccessPathDefUseTraversal::visitSingleValueUser(SingleValueInstruction *svi,
18641864
return IgnoredUse;
18651865
}
18661866

1867+
case SILInstructionKind::DropDeinitInst:
18671868
case SILInstructionKind::MarkMustCheckInst: {
18681869
// Mark must check goes on the project_box, so it isn't a ref.
18691870
assert(!dfs.isRef());

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,7 @@ struct ImmutableAddressUseVerifier {
667667
case SILInstructionKind::IndexAddrInst:
668668
case SILInstructionKind::TailAddrInst:
669669
case SILInstructionKind::IndexRawPointerInst:
670+
case SILInstructionKind::MarkMustCheckInst:
670671
// Add these to our worklist.
671672
for (auto result : inst->getResults()) {
672673
llvm::copy(result->getUses(), std::back_inserter(worklist));
@@ -5915,6 +5916,14 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
59155916
"Result and operand must have the same type, today.");
59165917
}
59175918

5919+
void checkDropDeinitInst(DropDeinitInst *ddi) {
5920+
require(ddi->getType() == ddi->getOperand()->getType(),
5921+
"Result and operand must have the same type.");
5922+
require(ddi->getType().isMoveOnlyNominalType(),
5923+
"drop_deinit only allowed for move-only types");
5924+
require(F.hasOwnership(), "drop_deinit only allowed in OSSA");
5925+
}
5926+
59185927
void checkMarkMustCheckInst(MarkMustCheckInst *i) {
59195928
require(i->getModule().getStage() == SILStage::Raw,
59205929
"Only valid in Raw SIL! Should have been eliminated by /some/ "

lib/SILGen/SILGenDestructor.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,7 @@ void SILGenFunction::emitMoveOnlyMemberDestruction(SILValue selfValue,
490490
NominalTypeDecl *nom,
491491
CleanupLocation cleanupLoc,
492492
SILBasicBlock *finishBB) {
493+
selfValue = B.createDropDeinit(cleanupLoc, selfValue);
493494
if (selfValue->getType().isAddress()) {
494495
if (auto *structDecl = dyn_cast<StructDecl>(nom)) {
495496
for (VarDecl *vd : nom->getStoredProperties()) {

lib/SILGen/SILGenProlog.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ static void diagnose(ASTContext &Context, SourceLoc loc, Diag<T...> diag,
4040

4141
SILValue SILGenFunction::emitSelfDeclForDestructor(VarDecl *selfDecl) {
4242
// Emit the implicit 'self' argument.
43-
SILType selfType = getLoweredLoadableType(selfDecl->getType());
43+
SILType selfType = getLoweredType(selfDecl->getType());
4444
SILValue selfValue = F.begin()->createFunctionArgument(selfType, selfDecl);
4545

4646
// If we have a move only type, then mark it with mark_must_check so we can't

lib/SILOptimizer/Mandatory/MoveOnlyDeinitInsertion.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ static bool performTransform(SILFunction &fn) {
6262

6363
if (auto *dvi = dyn_cast<DestroyValueInst>(inst)) {
6464
auto destroyType = dvi->getOperand()->getType();
65-
if (destroyType.isMoveOnlyNominalType()) {
65+
if (destroyType.isMoveOnlyNominalType() &&
66+
!isa<DropDeinitInst>(lookThroughOwnershipInsts(dvi->getOperand()))) {
6667
LLVM_DEBUG(llvm::dbgs() << "Handling: " << *dvi);
6768
auto *nom = destroyType.getNominalOrBoundGenericNominal();
6869
assert(nom);
@@ -88,7 +89,8 @@ static bool performTransform(SILFunction &fn) {
8889

8990
if (auto *dai = dyn_cast<DestroyAddrInst>(inst)) {
9091
auto destroyType = dai->getOperand()->getType();
91-
if (destroyType.isLoadable(fn) && destroyType.isMoveOnlyNominalType()) {
92+
if (destroyType.isLoadable(fn) && destroyType.isMoveOnlyNominalType() &&
93+
!isa<DropDeinitInst>(dai->getOperand())) {
9294
LLVM_DEBUG(llvm::dbgs() << "Handling: " << *dai);
9395
auto *nom = destroyType.getNominalOrBoundGenericNominal();
9496
assert(nom);

lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ struct OwnershipModelEliminatorVisitor
141141
eraseInstructionAndRAUW(mvi, mvi->getOperand());
142142
return true;
143143
}
144+
bool visitDropDeinitInst(DropDeinitInst *ddi) {
145+
eraseInstructionAndRAUW(ddi, ddi->getOperand());
146+
return true;
147+
}
144148
bool visitBeginBorrowInst(BeginBorrowInst *bbi) {
145149
eraseInstructionAndRAUW(bbi, bbi->getOperand());
146150
return true;

lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ static bool hasOpaqueArchetype(TypeExpansionContext context,
204204
case SILInstructionKind::CopyValueInst:
205205
case SILInstructionKind::ExplicitCopyValueInst:
206206
case SILInstructionKind::MoveValueInst:
207+
case SILInstructionKind::DropDeinitInst:
207208
case SILInstructionKind::MarkMustCheckInst:
208209
case SILInstructionKind::MarkUnresolvedReferenceBindingInst:
209210
case SILInstructionKind::CopyableToMoveOnlyWrapperValueInst:

lib/SILOptimizer/Utils/SILInliner.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) {
876876
case SILInstructionKind::BindMemoryInst:
877877
case SILInstructionKind::RebindMemoryInst:
878878
case SILInstructionKind::MoveValueInst:
879+
case SILInstructionKind::DropDeinitInst:
879880
case SILInstructionKind::MarkMustCheckInst:
880881
case SILInstructionKind::MarkUnresolvedReferenceBindingInst:
881882
case SILInstructionKind::CopyableToMoveOnlyWrapperValueInst:

lib/Serialization/DeserializeSIL.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2200,6 +2200,14 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn,
22002200
break;
22012201
}
22022202

2203+
case SILInstructionKind::DropDeinitInst: {
2204+
auto Ty = MF->getType(TyID);
2205+
ResultInst = Builder.createDropDeinit(
2206+
Loc,
2207+
getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory, Fn)));
2208+
break;
2209+
}
2210+
22032211
case SILInstructionKind::MarkUnresolvedReferenceBindingInst: {
22042212
using Kind = MarkUnresolvedReferenceBindingInst::Kind;
22052213
auto ty = MF->getType(TyID);

lib/Serialization/ModuleFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5858
/// describe what change you made. The content of this comment isn't important;
5959
/// it just ensures a conflict if two people change the module format.
6060
/// Don't worry about adhering to the 80-column limit for this line.
61-
const uint16_t SWIFTMODULE_VERSION_MINOR = 757; // expanded macro definitions
61+
const uint16_t SWIFTMODULE_VERSION_MINOR = 758; // drop_deinit instruction
6262

6363
/// A standard hash seed used for all string hashes in a serialized module.
6464
///

lib/Serialization/SerializeSIL.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1482,6 +1482,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) {
14821482
case SILInstructionKind::CopyValueInst:
14831483
case SILInstructionKind::ExplicitCopyValueInst:
14841484
case SILInstructionKind::MoveValueInst:
1485+
case SILInstructionKind::DropDeinitInst:
14851486
case SILInstructionKind::MarkUnresolvedReferenceBindingInst:
14861487
case SILInstructionKind::MoveOnlyWrapperToCopyableValueInst:
14871488
case SILInstructionKind::CopyableToMoveOnlyWrapperValueInst:

test/SIL/Parser/basic.sil

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ class Class2 {
4848
init()
4949
}
5050

51+
@_moveOnly struct MoveOnlyStruct {
52+
@_hasStorage var i: Int
53+
deinit
54+
}
55+
56+
5157
sil @type_ref1 : $(Class1, Int) -> () // CHECK-LABEL: sil @type_ref1 : $@convention(thin) (Class1, Int)
5258

5359
// Instructions
@@ -1659,6 +1665,16 @@ bb0(%0 : @guaranteed $Builtin.NativeObject):
16591665
return undef : $()
16601666
}
16611667

1668+
// CHECK-LABEL: sil [ossa] @test_drop_deinit :
1669+
sil [ossa] @test_drop_deinit : $@convention(thin) (@owned MoveOnlyStruct) -> () {
1670+
bb0(%0 : @owned $MoveOnlyStruct):
1671+
// CHECK: drop_deinit %0 : $MoveOnlyStruct
1672+
%1 = drop_deinit %0 : $MoveOnlyStruct
1673+
destroy_value %1 : $MoveOnlyStruct
1674+
%3 = tuple ()
1675+
return %3 : $()
1676+
}
1677+
16621678
sil @test_destructure_struct_tuple : $@convention(thin) (@owned (Builtin.NativeObject, Builtin.Int32), @owned TestArray2) -> @owned (Builtin.NativeObject, Builtin.Int32, TestArrayStorage, Int32, TestArrayStorage) {
16631679
bb0(%0 : $(Builtin.NativeObject, Builtin.Int32), %1 : $TestArray2):
16641680
(%2, %3) = destructure_tuple %0 : $(Builtin.NativeObject, Builtin.Int32)

test/SIL/Serialization/basic.sil

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ struct Int32 {
2323

2424
struct EmptyStruct {}
2525

26+
@_moveOnly struct MoveOnlyStruct {
27+
@_hasStorage var i: Int32
28+
deinit
29+
}
30+
2631
// CHECK-LABEL: sil @async_test : $@convention(thin) @async
2732
sil @async_test : $@async () -> () {
2833
bb0:
@@ -44,6 +49,17 @@ bb0(%0 : @owned $(Builtin.NativeObject, Builtin.Int32), %1 : @owned $TestArray2)
4449
return %7 : $(Builtin.NativeObject, Builtin.Int32, TestArrayStorage, Int32, TestArrayStorage)
4550
}
4651

52+
// CHECK-LABEL: sil [ossa] @test_drop_deinit :
53+
// CHECK: %1 = drop_deinit %0 : $MoveOnlyStruct
54+
// CHECK-LABEL: } // end sil function 'test_drop_deinit'
55+
sil [ossa] @test_drop_deinit : $@convention(thin) (@owned MoveOnlyStruct) -> () {
56+
bb0(%0 : @owned $MoveOnlyStruct):
57+
%1 = drop_deinit %0 : $MoveOnlyStruct
58+
destroy_value %1 : $MoveOnlyStruct
59+
%3 = tuple ()
60+
return %3 : $()
61+
}
62+
4763
sil @test_empty_destructure : $@convention(thin) () -> () {
4864
bb0:
4965
%0 = struct $EmptyStruct()

0 commit comments

Comments
 (0)