mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
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>.
This commit is contained in:
@@ -1566,41 +1566,42 @@ emitCastOperand(SILGenFunction &SGF, SILLocation loc,
|
|||||||
SGFContext ctx;
|
SGFContext ctx;
|
||||||
if (requiresAddress) {
|
if (requiresAddress) {
|
||||||
init = SGF.emitTemporary(loc, srcAbstractTL);
|
init = SGF.emitTemporary(loc, srcAbstractTL);
|
||||||
|
|
||||||
// Okay, if all we need to do is drop the value in an address,
|
|
||||||
// this is easy.
|
|
||||||
if (!hasAbstraction) {
|
|
||||||
// TODO: Refactor this into a materialize method on CastConsumptionKind.
|
|
||||||
ManagedValue finalValue = src.getFinalManagedValue();
|
|
||||||
if (finalValue.getOwnershipKind() == ValueOwnershipKind::Guaranteed)
|
|
||||||
finalValue = finalValue.copy(SGF, loc);
|
|
||||||
SGF.B.emitStoreValueOperation(loc, finalValue.forward(SGF),
|
|
||||||
init->getAddress(),
|
|
||||||
StoreOwnershipQualifier::Init);
|
|
||||||
init->finishInitialization(SGF);
|
|
||||||
// If we had borrow_always, we need to switch to copy_on_success since
|
|
||||||
// that is the address only variant of borrow_always.
|
|
||||||
auto consumption = src.getFinalConsumption();
|
|
||||||
if (consumption == CastConsumptionKind::BorrowAlways)
|
|
||||||
consumption = CastConsumptionKind::CopyOnSuccess;
|
|
||||||
ConsumableManagedValue result = {init->getManagedAddress(), consumption};
|
|
||||||
if (ArgUnforwarder::requiresUnforwarding(SGF, src))
|
|
||||||
borrowedValues.push_back(result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = SGFContext(init.get());
|
ctx = SGFContext(init.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(hasAbstraction);
|
|
||||||
assert(src.getType().isObject() &&
|
|
||||||
"address-only type with abstraction difference?");
|
|
||||||
|
|
||||||
// Produce the value at +1.
|
|
||||||
ManagedValue substValue = SGF.getManagedValue(loc, src);
|
ManagedValue substValue = SGF.getManagedValue(loc, src);
|
||||||
ManagedValue origValue =
|
ManagedValue finalValue;
|
||||||
SGF.emitSubstToOrigValue(loc, substValue, abstraction, sourceType);
|
if (hasAbstraction) {
|
||||||
return ConsumableManagedValue::forOwned(origValue);
|
assert(src.getType().isObject() &&
|
||||||
|
"address-only type with abstraction difference?");
|
||||||
|
// Produce the value at +1.
|
||||||
|
finalValue = SGF.emitSubstToOrigValue(loc, substValue,
|
||||||
|
abstraction, sourceType, ctx);
|
||||||
|
} else {
|
||||||
|
finalValue = substValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requiresAddress) {
|
||||||
|
if (finalValue.getOwnershipKind() == ValueOwnershipKind::Guaranteed)
|
||||||
|
finalValue = finalValue.copy(SGF, loc);
|
||||||
|
SGF.B.emitStoreValueOperation(loc, finalValue.forward(SGF),
|
||||||
|
init->getAddress(),
|
||||||
|
StoreOwnershipQualifier::Init);
|
||||||
|
init->finishInitialization(SGF);
|
||||||
|
|
||||||
|
// If we had borrow_always, we need to switch to copy_on_success since
|
||||||
|
// that is the address only variant of borrow_always.
|
||||||
|
auto consumption = src.getFinalConsumption();
|
||||||
|
if (consumption == CastConsumptionKind::BorrowAlways)
|
||||||
|
consumption = CastConsumptionKind::CopyOnSuccess;
|
||||||
|
ConsumableManagedValue result = {init->getManagedAddress(), consumption};
|
||||||
|
if (ArgUnforwarder::requiresUnforwarding(SGF, src))
|
||||||
|
borrowedValues.push_back(result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConsumableManagedValue::forOwned(finalValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform specialized dispatch for a sequence of IsPatterns.
|
/// Perform specialized dispatch for a sequence of IsPatterns.
|
||||||
@@ -2536,6 +2537,7 @@ public:
|
|||||||
static void emitDiagnoseOfUnexpectedEnumCaseValue(SILGenFunction &SGF,
|
static void emitDiagnoseOfUnexpectedEnumCaseValue(SILGenFunction &SGF,
|
||||||
SILLocation loc,
|
SILLocation loc,
|
||||||
ManagedValue value,
|
ManagedValue value,
|
||||||
|
Type subjectTy,
|
||||||
const EnumDecl *enumDecl) {
|
const EnumDecl *enumDecl) {
|
||||||
ASTContext &ctx = SGF.getASTContext();
|
ASTContext &ctx = SGF.getASTContext();
|
||||||
auto diagnoseFailure = ctx.getDiagnoseUnexpectedEnumCaseValue(nullptr);
|
auto diagnoseFailure = ctx.getDiagnoseUnexpectedEnumCaseValue(nullptr);
|
||||||
@@ -2549,10 +2551,9 @@ static void emitDiagnoseOfUnexpectedEnumCaseValue(SILGenFunction &SGF,
|
|||||||
assert(value.getType().isTrivial(SGF.getModule()));
|
assert(value.getType().isTrivial(SGF.getModule()));
|
||||||
|
|
||||||
// Get the enum type as an Any.Type value.
|
// Get the enum type as an Any.Type value.
|
||||||
CanType switchedValueSwiftType = value.getType().getASTType();
|
|
||||||
SILType metatypeType = SGF.getLoweredType(
|
SILType metatypeType = SGF.getLoweredType(
|
||||||
CanMetatypeType::get(switchedValueSwiftType,
|
AbstractionPattern::getOpaque(),
|
||||||
MetatypeRepresentation::Thick));
|
MetatypeType::get(subjectTy));
|
||||||
SILValue metatype = SGF.B.createMetatype(loc, metatypeType);
|
SILValue metatype = SGF.B.createMetatype(loc, metatypeType);
|
||||||
|
|
||||||
// Bitcast the enum value to its raw type. (This is only safe for @objc
|
// Bitcast the enum value to its raw type. (This is only safe for @objc
|
||||||
@@ -2573,7 +2574,7 @@ static void emitDiagnoseOfUnexpectedEnumCaseValue(SILGenFunction &SGF,
|
|||||||
assert(genericParam->getIndex() < 2);
|
assert(genericParam->getIndex() < 2);
|
||||||
switch (genericParam->getIndex()) {
|
switch (genericParam->getIndex()) {
|
||||||
case 0:
|
case 0:
|
||||||
return switchedValueSwiftType;
|
return subjectTy;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
return enumDecl->getRawType();
|
return enumDecl->getRawType();
|
||||||
@@ -2592,7 +2593,8 @@ static void emitDiagnoseOfUnexpectedEnumCaseValue(SILGenFunction &SGF,
|
|||||||
|
|
||||||
static void emitDiagnoseOfUnexpectedEnumCase(SILGenFunction &SGF,
|
static void emitDiagnoseOfUnexpectedEnumCase(SILGenFunction &SGF,
|
||||||
SILLocation loc,
|
SILLocation loc,
|
||||||
ManagedValue value) {
|
ManagedValue value,
|
||||||
|
Type subjectTy) {
|
||||||
ASTContext &ctx = SGF.getASTContext();
|
ASTContext &ctx = SGF.getASTContext();
|
||||||
auto diagnoseFailure = ctx.getDiagnoseUnexpectedEnumCase(nullptr);
|
auto diagnoseFailure = ctx.getDiagnoseUnexpectedEnumCase(nullptr);
|
||||||
if (!diagnoseFailure) {
|
if (!diagnoseFailure) {
|
||||||
@@ -2601,16 +2603,15 @@ static void emitDiagnoseOfUnexpectedEnumCase(SILGenFunction &SGF,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the switched-upon value's type.
|
// Get the switched-upon value's type.
|
||||||
CanType switchedValueSwiftType = value.getType().getASTType();
|
|
||||||
SILType metatypeType = SGF.getLoweredType(
|
SILType metatypeType = SGF.getLoweredType(
|
||||||
CanMetatypeType::get(switchedValueSwiftType,
|
AbstractionPattern::getOpaque(),
|
||||||
MetatypeRepresentation::Thick));
|
MetatypeType::get(subjectTy)->getCanonicalType());
|
||||||
ManagedValue metatype = SGF.B.createValueMetatype(loc, metatypeType, value);
|
ManagedValue metatype = SGF.B.createValueMetatype(loc, metatypeType, value);
|
||||||
|
|
||||||
auto diagnoseSignature = diagnoseFailure->getGenericSignature();
|
auto diagnoseSignature = diagnoseFailure->getGenericSignature();
|
||||||
auto genericArgsMap = SubstitutionMap::get(
|
auto genericArgsMap = SubstitutionMap::get(
|
||||||
diagnoseSignature,
|
diagnoseSignature,
|
||||||
[&](SubstitutableType *type) -> Type { return switchedValueSwiftType; },
|
[&](SubstitutableType *type) -> Type { return subjectTy; },
|
||||||
LookUpConformanceInSignature(*diagnoseSignature));
|
LookUpConformanceInSignature(*diagnoseSignature));
|
||||||
|
|
||||||
SGF.emitApplyOfLibraryIntrinsic(loc, diagnoseFailure, genericArgsMap,
|
SGF.emitApplyOfLibraryIntrinsic(loc, diagnoseFailure, genericArgsMap,
|
||||||
@@ -2622,9 +2623,12 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
|
|||||||
LLVM_DEBUG(llvm::dbgs() << "emitting switch stmt\n";
|
LLVM_DEBUG(llvm::dbgs() << "emitting switch stmt\n";
|
||||||
S->dump(llvm::dbgs());
|
S->dump(llvm::dbgs());
|
||||||
llvm::dbgs() << '\n');
|
llvm::dbgs() << '\n');
|
||||||
|
|
||||||
|
auto subjectTy = S->getSubjectExpr()->getType();
|
||||||
|
|
||||||
// If the subject expression is uninhabited, we're already dead.
|
// If the subject expression is uninhabited, we're already dead.
|
||||||
// Emit an unreachable in place of the switch statement.
|
// Emit an unreachable in place of the switch statement.
|
||||||
if (S->getSubjectExpr()->getType()->isStructurallyUninhabited()) {
|
if (subjectTy->isStructurallyUninhabited()) {
|
||||||
emitIgnoredExpr(S->getSubjectExpr());
|
emitIgnoredExpr(S->getSubjectExpr());
|
||||||
B.createUnreachable(S);
|
B.createUnreachable(S);
|
||||||
return;
|
return;
|
||||||
@@ -2789,12 +2793,13 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
|
|||||||
if (singleEnumDecl->isObjC()) {
|
if (singleEnumDecl->isObjC()) {
|
||||||
emitDiagnoseOfUnexpectedEnumCaseValue(*this, location,
|
emitDiagnoseOfUnexpectedEnumCaseValue(*this, location,
|
||||||
subject.getFinalManagedValue(),
|
subject.getFinalManagedValue(),
|
||||||
singleEnumDecl);
|
subjectTy, singleEnumDecl);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emitDiagnoseOfUnexpectedEnumCase(*this, location,
|
emitDiagnoseOfUnexpectedEnumCase(*this, location,
|
||||||
subject.getFinalManagedValue());
|
subject.getFinalManagedValue(),
|
||||||
|
subjectTy);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set up an initial clause matrix.
|
// Set up an initial clause matrix.
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ enum Optionable<T> {
|
|||||||
// CHECK: [[ORIG:%.*]] = copy_value [[ARG]]
|
// CHECK: [[ORIG:%.*]] = copy_value [[ARG]]
|
||||||
// CHECK: [[REABSTRACT:%.*]] = function_ref @$s{{.*}}TR :
|
// CHECK: [[REABSTRACT:%.*]] = function_ref @$s{{.*}}TR :
|
||||||
// CHECK: [[SUBST:%.*]] = partial_apply [callee_guaranteed] [[REABSTRACT]]([[ORIG]])
|
// CHECK: [[SUBST:%.*]] = partial_apply [callee_guaranteed] [[REABSTRACT]]([[ORIG]])
|
||||||
func enum_reabstraction(x x: Optionable<(A) -> A>, a: A) {
|
func enum_reabstraction(x: Optionable<(A) -> A>, a: A) {
|
||||||
switch x {
|
switch x {
|
||||||
case .Summn(var f):
|
case .Summn(var f):
|
||||||
f(a)
|
f(a)
|
||||||
@@ -37,7 +37,7 @@ enum Wacky<A, B> {
|
|||||||
// CHECK: [[ORIG:%.*]] = load [take] [[ORIG_ADDR]]
|
// CHECK: [[ORIG:%.*]] = load [take] [[ORIG_ADDR]]
|
||||||
// CHECK: [[REABSTRACT:%.*]] = function_ref @$s{{.*}}TR :
|
// CHECK: [[REABSTRACT:%.*]] = function_ref @$s{{.*}}TR :
|
||||||
// CHECK: [[SUBST:%.*]] = partial_apply [callee_guaranteed] [[REABSTRACT]]<T>([[ORIG]])
|
// CHECK: [[SUBST:%.*]] = partial_apply [callee_guaranteed] [[REABSTRACT]]<T>([[ORIG]])
|
||||||
func enum_addr_only_to_loadable_with_reabstraction<T>(x x: Wacky<T, A>, a: A)
|
func enum_addr_only_to_loadable_with_reabstraction<T>(x: Wacky<T, A>, a: A)
|
||||||
-> T
|
-> T
|
||||||
{
|
{
|
||||||
switch x {
|
switch x {
|
||||||
@@ -47,3 +47,19 @@ func enum_addr_only_to_loadable_with_reabstraction<T>(x x: Wacky<T, A>, a: A)
|
|||||||
return f(a)
|
return f(a)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hello() {}
|
||||||
|
func goodbye(_: Any) {}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil hidden @$s18switch_abstraction34requires_address_and_reabstractionyyF : $@convention(thin) () -> () {
|
||||||
|
// CHECK: [[FN:%.*]] = function_ref @$s18switch_abstraction5helloyyF : $@convention(thin) () -> ()
|
||||||
|
// CHECK: [[THICK:%.*]] = thin_to_thick_function [[FN]]
|
||||||
|
// CHECK: [[BOX:%.*]] = alloc_stack $@callee_guaranteed () -> @out ()
|
||||||
|
// CHECK: [[THUNK:%.*]] = function_ref @$sIeg_ytIegr_TR : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> @out ()
|
||||||
|
// CHECK: [[ABSTRACT:%.*]] = partial_apply [callee_guaranteed] [[THUNK]]([[THICK]])
|
||||||
|
// CHECK: store [[ABSTRACT]] to [init] [[BOX]]
|
||||||
|
func requires_address_and_reabstraction() {
|
||||||
|
switch hello {
|
||||||
|
case let a as Any: goodbye(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user