Skip to content

Commit 14a3a5d

Browse files
committed
SILGen: Fix corner case when emitting switch over value needing reabstraction
This also exposed a bug where we use a lowered type as an AST type and crash when emitting the '@unknown case' block. This block is unreachable when switching over non-enum values, but its emitted anyway. Fixes <https://bugs.swift.org/browse/SR-9159>, <rdar://problem/45962466>.
1 parent 1673c12 commit 14a3a5d

File tree

2 files changed

+66
-45
lines changed

2 files changed

+66
-45
lines changed

lib/SILGen/SILGenPattern.cpp

Lines changed: 48 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1566,41 +1566,42 @@ emitCastOperand(SILGenFunction &SGF, SILLocation loc,
15661566
SGFContext ctx;
15671567
if (requiresAddress) {
15681568
init = SGF.emitTemporary(loc, srcAbstractTL);
1569-
1570-
// Okay, if all we need to do is drop the value in an address,
1571-
// this is easy.
1572-
if (!hasAbstraction) {
1573-
// TODO: Refactor this into a materialize method on CastConsumptionKind.
1574-
ManagedValue finalValue = src.getFinalManagedValue();
1575-
if (finalValue.getOwnershipKind() == ValueOwnershipKind::Guaranteed)
1576-
finalValue = finalValue.copy(SGF, loc);
1577-
SGF.B.emitStoreValueOperation(loc, finalValue.forward(SGF),
1578-
init->getAddress(),
1579-
StoreOwnershipQualifier::Init);
1580-
init->finishInitialization(SGF);
1581-
// If we had borrow_always, we need to switch to copy_on_success since
1582-
// that is the address only variant of borrow_always.
1583-
auto consumption = src.getFinalConsumption();
1584-
if (consumption == CastConsumptionKind::BorrowAlways)
1585-
consumption = CastConsumptionKind::CopyOnSuccess;
1586-
ConsumableManagedValue result = {init->getManagedAddress(), consumption};
1587-
if (ArgUnforwarder::requiresUnforwarding(SGF, src))
1588-
borrowedValues.push_back(result);
1589-
return result;
1590-
}
1591-
15921569
ctx = SGFContext(init.get());
15931570
}
15941571

1595-
assert(hasAbstraction);
1596-
assert(src.getType().isObject() &&
1597-
"address-only type with abstraction difference?");
1598-
1599-
// Produce the value at +1.
16001572
ManagedValue substValue = SGF.getManagedValue(loc, src);
1601-
ManagedValue origValue =
1602-
SGF.emitSubstToOrigValue(loc, substValue, abstraction, sourceType);
1603-
return ConsumableManagedValue::forOwned(origValue);
1573+
ManagedValue finalValue;
1574+
if (hasAbstraction) {
1575+
assert(src.getType().isObject() &&
1576+
"address-only type with abstraction difference?");
1577+
// Produce the value at +1.
1578+
finalValue = SGF.emitSubstToOrigValue(loc, substValue,
1579+
abstraction, sourceType, ctx);
1580+
} else {
1581+
finalValue = substValue;
1582+
}
1583+
1584+
if (requiresAddress) {
1585+
if (finalValue.getOwnershipKind() == ValueOwnershipKind::Guaranteed)
1586+
finalValue = finalValue.copy(SGF, loc);
1587+
SGF.B.emitStoreValueOperation(loc, finalValue.forward(SGF),
1588+
init->getAddress(),
1589+
StoreOwnershipQualifier::Init);
1590+
init->finishInitialization(SGF);
1591+
1592+
// If we had borrow_always, we need to switch to copy_on_success since
1593+
// that is the address only variant of borrow_always.
1594+
auto consumption = src.getFinalConsumption();
1595+
if (consumption == CastConsumptionKind::BorrowAlways)
1596+
consumption = CastConsumptionKind::CopyOnSuccess;
1597+
ConsumableManagedValue result = {init->getManagedAddress(), consumption};
1598+
if (ArgUnforwarder::requiresUnforwarding(SGF, src))
1599+
borrowedValues.push_back(result);
1600+
1601+
return result;
1602+
}
1603+
1604+
return ConsumableManagedValue::forOwned(finalValue);
16041605
}
16051606

16061607
/// Perform specialized dispatch for a sequence of IsPatterns.
@@ -2536,6 +2537,7 @@ class Lowering::PatternMatchContext {
25362537
static void emitDiagnoseOfUnexpectedEnumCaseValue(SILGenFunction &SGF,
25372538
SILLocation loc,
25382539
ManagedValue value,
2540+
Type subjectTy,
25392541
const EnumDecl *enumDecl) {
25402542
ASTContext &ctx = SGF.getASTContext();
25412543
auto diagnoseFailure = ctx.getDiagnoseUnexpectedEnumCaseValue(nullptr);
@@ -2549,10 +2551,9 @@ static void emitDiagnoseOfUnexpectedEnumCaseValue(SILGenFunction &SGF,
25492551
assert(value.getType().isTrivial(SGF.getModule()));
25502552

25512553
// Get the enum type as an Any.Type value.
2552-
CanType switchedValueSwiftType = value.getType().getASTType();
25532554
SILType metatypeType = SGF.getLoweredType(
2554-
CanMetatypeType::get(switchedValueSwiftType,
2555-
MetatypeRepresentation::Thick));
2555+
AbstractionPattern::getOpaque(),
2556+
MetatypeType::get(subjectTy));
25562557
SILValue metatype = SGF.B.createMetatype(loc, metatypeType);
25572558

25582559
// Bitcast the enum value to its raw type. (This is only safe for @objc
@@ -2573,7 +2574,7 @@ static void emitDiagnoseOfUnexpectedEnumCaseValue(SILGenFunction &SGF,
25732574
assert(genericParam->getIndex() < 2);
25742575
switch (genericParam->getIndex()) {
25752576
case 0:
2576-
return switchedValueSwiftType;
2577+
return subjectTy;
25772578

25782579
case 1:
25792580
return enumDecl->getRawType();
@@ -2592,7 +2593,8 @@ static void emitDiagnoseOfUnexpectedEnumCaseValue(SILGenFunction &SGF,
25922593

25932594
static void emitDiagnoseOfUnexpectedEnumCase(SILGenFunction &SGF,
25942595
SILLocation loc,
2595-
ManagedValue value) {
2596+
ManagedValue value,
2597+
Type subjectTy) {
25962598
ASTContext &ctx = SGF.getASTContext();
25972599
auto diagnoseFailure = ctx.getDiagnoseUnexpectedEnumCase(nullptr);
25982600
if (!diagnoseFailure) {
@@ -2601,16 +2603,15 @@ static void emitDiagnoseOfUnexpectedEnumCase(SILGenFunction &SGF,
26012603
}
26022604

26032605
// Get the switched-upon value's type.
2604-
CanType switchedValueSwiftType = value.getType().getASTType();
26052606
SILType metatypeType = SGF.getLoweredType(
2606-
CanMetatypeType::get(switchedValueSwiftType,
2607-
MetatypeRepresentation::Thick));
2607+
AbstractionPattern::getOpaque(),
2608+
MetatypeType::get(subjectTy)->getCanonicalType());
26082609
ManagedValue metatype = SGF.B.createValueMetatype(loc, metatypeType, value);
26092610

26102611
auto diagnoseSignature = diagnoseFailure->getGenericSignature();
26112612
auto genericArgsMap = SubstitutionMap::get(
26122613
diagnoseSignature,
2613-
[&](SubstitutableType *type) -> Type { return switchedValueSwiftType; },
2614+
[&](SubstitutableType *type) -> Type { return subjectTy; },
26142615
LookUpConformanceInSignature(*diagnoseSignature));
26152616

26162617
SGF.emitApplyOfLibraryIntrinsic(loc, diagnoseFailure, genericArgsMap,
@@ -2622,9 +2623,12 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
26222623
LLVM_DEBUG(llvm::dbgs() << "emitting switch stmt\n";
26232624
S->dump(llvm::dbgs());
26242625
llvm::dbgs() << '\n');
2626+
2627+
auto subjectTy = S->getSubjectExpr()->getType();
2628+
26252629
// If the subject expression is uninhabited, we're already dead.
26262630
// Emit an unreachable in place of the switch statement.
2627-
if (S->getSubjectExpr()->getType()->isStructurallyUninhabited()) {
2631+
if (subjectTy->isStructurallyUninhabited()) {
26282632
emitIgnoredExpr(S->getSubjectExpr());
26292633
B.createUnreachable(S);
26302634
return;
@@ -2789,12 +2793,13 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
27892793
if (singleEnumDecl->isObjC()) {
27902794
emitDiagnoseOfUnexpectedEnumCaseValue(*this, location,
27912795
subject.getFinalManagedValue(),
2792-
singleEnumDecl);
2796+
subjectTy, singleEnumDecl);
27932797
return;
27942798
}
27952799
}
27962800
emitDiagnoseOfUnexpectedEnumCase(*this, location,
2797-
subject.getFinalManagedValue());
2801+
subject.getFinalManagedValue(),
2802+
subjectTy);
27982803
};
27992804

28002805
// Set up an initial clause matrix.

test/SILGen/switch_abstraction.swift

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ enum Optionable<T> {
1616
// CHECK: [[ORIG:%.*]] = copy_value [[ARG]]
1717
// CHECK: [[REABSTRACT:%.*]] = function_ref @$s{{.*}}TR :
1818
// CHECK: [[SUBST:%.*]] = partial_apply [callee_guaranteed] [[REABSTRACT]]([[ORIG]])
19-
func enum_reabstraction(x x: Optionable<(A) -> A>, a: A) {
19+
func enum_reabstraction(x: Optionable<(A) -> A>, a: A) {
2020
switch x {
2121
case .Summn(var f):
2222
f(a)
@@ -37,7 +37,7 @@ enum Wacky<A, B> {
3737
// CHECK: [[ORIG:%.*]] = load [take] [[ORIG_ADDR]]
3838
// CHECK: [[REABSTRACT:%.*]] = function_ref @$s{{.*}}TR :
3939
// CHECK: [[SUBST:%.*]] = partial_apply [callee_guaranteed] [[REABSTRACT]]<T>([[ORIG]])
40-
func enum_addr_only_to_loadable_with_reabstraction<T>(x x: Wacky<T, A>, a: A)
40+
func enum_addr_only_to_loadable_with_reabstraction<T>(x: Wacky<T, A>, a: A)
4141
-> T
4242
{
4343
switch x {
@@ -47,3 +47,19 @@ func enum_addr_only_to_loadable_with_reabstraction<T>(x x: Wacky<T, A>, a: A)
4747
return f(a)
4848
}
4949
}
50+
51+
func hello() {}
52+
func goodbye(_: Any) {}
53+
54+
// CHECK-LABEL: sil hidden @$s18switch_abstraction34requires_address_and_reabstractionyyF : $@convention(thin) () -> () {
55+
// CHECK: [[FN:%.*]] = function_ref @$s18switch_abstraction5helloyyF : $@convention(thin) () -> ()
56+
// CHECK: [[THICK:%.*]] = thin_to_thick_function [[FN]]
57+
// CHECK: [[BOX:%.*]] = alloc_stack $@callee_guaranteed () -> @out ()
58+
// CHECK: [[THUNK:%.*]] = function_ref @$sIeg_ytIegr_TR : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> @out ()
59+
// CHECK: [[ABSTRACT:%.*]] = partial_apply [callee_guaranteed] [[THUNK]]([[THICK]])
60+
// CHECK: store [[ABSTRACT]] to [init] [[BOX]]
61+
func requires_address_and_reabstraction() {
62+
switch hello {
63+
case let a as Any: goodbye(a)
64+
}
65+
}

0 commit comments

Comments
 (0)