AST/SILGen: Use @_alwaysEmitIntoClient diagnostic helper in unavailable code.

The `_diagnoseUnavailableCodeReached()` function was introduced in the Swift
5.9 standard library and employs `@backDeployed` to support compilation of
binaries that target OS releases aligned with earlier Swift releases.
Unfortunately, though, this backdeployment strategy doesn't work well for some
unusual build environments. Specifically, in some configurations code may be
built with a compiler from a recent Swift toolchain and then linked against the
dylibs in an older toolchain. When linking against the older dylibs, the
`_diagnoseUnavailableCodeReached()` function does not exist but the
`@backDeployed` thunks emitted into the binary reference that function and
therefore linking fails.

The idea of building with one toolchain and then linking to the dylibs in a
different, older toolchain is extremely dubious. However, it exists and for now
we need to support it. This PR introduces an alternative
`_diagnoseUnavailableCodeReached()` function that is annotated with
`@_alwaysEmitIntoClient`. Calls to the AEIC variant are now emitted by the
compiler when the deployment target is before Swift 5.9.

Once these unusual build environments upgrade and start linking against a Swift
5.9 toolchain or later we can revert all of this.

Resolves rdar://119046537
This commit is contained in:
Allan Shortlidge
2023-12-19 13:33:04 -08:00
parent a37aa1c1fb
commit 6d22433d0f
16 changed files with 54 additions and 23 deletions

View File

@@ -1526,6 +1526,10 @@ public:
/// The declared interface type of Builtin.TheTupleType.
BuiltinTupleType *getBuiltinTupleType();
/// The declaration for the `_diagnoseUnavailableCodeReached()` declaration
/// that ought to be used for the configured deployment target.
FuncDecl *getDiagnoseUnavailableCodeReachedDecl();
Type getNamedSwiftType(ModuleDecl *module, StringRef name);
/// Set the plugin loader.

View File

@@ -70,6 +70,7 @@ FUNC_DECL(DiagnoseUnexpectedNilOptional, "_diagnoseUnexpectedNilOptional")
FUNC_DECL(DiagnoseUnexpectedEnumCase, "_diagnoseUnexpectedEnumCase")
FUNC_DECL(DiagnoseUnexpectedEnumCaseValue, "_diagnoseUnexpectedEnumCaseValue")
FUNC_DECL(DiagnoseUnavailableCodeReached, "_diagnoseUnavailableCodeReached")
FUNC_DECL(DiagnoseUnavailableCodeReachedAEIC, "_diagnoseUnavailableCodeReached_aeic")
FUNC_DECL(GetErrorEmbeddedNSError, "_getErrorEmbeddedNSError")

View File

@@ -6312,6 +6312,14 @@ BuiltinTupleType *ASTContext::getBuiltinTupleType() {
return result;
}
FuncDecl *ASTContext::getDiagnoseUnavailableCodeReachedDecl() {
// FIXME: Remove this with rdar://119892482
if (AvailabilityContext::forDeploymentTarget(*this).isContainedIn(
getSwift59Availability()))
return getDiagnoseUnavailableCodeReached();
return getDiagnoseUnavailableCodeReachedAEIC();
}
void ASTContext::setPluginLoader(std::unique_ptr<PluginLoader> loader) {
getImpl().Plugins = std::move(loader);
}

View File

@@ -6111,7 +6111,7 @@ void SILGenFunction::emitApplyOfUnavailableCodeReached() {
return;
auto loc = RegularLocation::getAutoGeneratedLocation(F.getLocation());
FuncDecl *fd = getASTContext().getDiagnoseUnavailableCodeReached();
FuncDecl *fd = getASTContext().getDiagnoseUnavailableCodeReachedDecl();
if (!fd) {
// Broken stdlib?

View File

@@ -495,7 +495,7 @@ DerivedConformance::createBuiltinCall(ASTContext &ctx,
CallExpr *DerivedConformance::createDiagnoseUnavailableCodeReachedCallExpr(
ASTContext &ctx) {
FuncDecl *diagnoseDecl = ctx.getDiagnoseUnavailableCodeReached();
FuncDecl *diagnoseDecl = ctx.getDiagnoseUnavailableCodeReachedDecl();
auto diagnoseDeclRefExpr =
new (ctx) DeclRefExpr(diagnoseDecl, DeclNameLoc(), true);
diagnoseDeclRefExpr->setType(diagnoseDecl->getInterfaceType());

View File

@@ -329,6 +329,17 @@ internal func _diagnoseUnexpectedEnumCase<SwitchedValue>(
@_semantics("unavailable_code_reached")
@usableFromInline // COMPILER_INTRINSIC
internal func _diagnoseUnavailableCodeReached() -> Never {
_diagnoseUnavailableCodeReached_aeic()
}
// FIXME: Remove this with rdar://119892482
/// An `@_alwaysEmitIntoClient` variant of `_diagnoseUnavailableCodeReached()`.
/// This is temporarily needed by the compiler to reference from back deployed
/// clients.
@_alwaysEmitIntoClient
@inline(never)
@_semantics("unavailable_code_reached")
internal func _diagnoseUnavailableCodeReached_aeic() -> Never {
_assertionFailure(
"Fatal error", "Unavailable code reached", flags: _fatalErrorFlags())
}

View File

@@ -19,5 +19,5 @@ typedef MyOptions MyOptionsTypedef;
let _ = MyOptionsTypedef(rawValue: 1)
// CHECK-LABEL: sil shared [transparent] [serialized]{{.*}} @$sSo9MyOptionsa8rawValueABs5Int32V_tcfC : $@convention(method) (Int32, @thin MyOptions.Type) -> MyOptions {
// CHECK-NOT: ss31_diagnoseUnavailableCodeReacheds5NeverOyFTwb
// CHECK-NOT: _diagnoseUnavailableCodeReached
// CHECK: } // end sil function '$sSo9MyOptionsa8rawValueABs5Int32V_tcfC'

View File

@@ -3,7 +3,7 @@
public struct S {}
// CHECK-LABEL: sil{{.*}}@$s4Test15unavailableFuncAA1SVyF
// CHECK: [[FNREF:%.*]] = function_ref @$[[DIAGNOSEFN:ss31_diagnoseUnavailableCodeReacheds5NeverOy(FTwb|F)]] : $@convention(thin) () -> Never
// CHECK: [[FNREF:%.*]] = function_ref @$[[DIAGNOSEFN:(ss36_diagnoseUnavailableCodeReached_aeics5NeverOyF|ss31_diagnoseUnavailableCodeReacheds5NeverOyF)]] : $@convention(thin) () -> Never
// CHECK-NEXT: [[APPLY:%.*]] = apply [[FNREF]]()
// CHECK: function_ref @$s4Test1SVACycfC
// CHECK: } // end sil function '$s4Test15unavailableFuncAA1SVyF'

View File

@@ -7,7 +7,7 @@ func foo() {}
public class ExplicitInitClass {
// ExplicitInitClass.s.getter
// CHECK-LABEL: sil{{.*}}@$s4Test17ExplicitInitClassC1sAA1SVvg
// CHECK: [[FNREF:%.*]] = function_ref @$[[DIAGNOSEFN:ss31_diagnoseUnavailableCodeReacheds5NeverOy(FTwb|F)]] : $@convention(thin) () -> Never
// CHECK: [[FNREF:%.*]] = function_ref @$[[DIAGNOSEFN:(ss36_diagnoseUnavailableCodeReached_aeics5NeverOyF|ss31_diagnoseUnavailableCodeReacheds5NeverOyF)]] : $@convention(thin) () -> Never
// CHECK-NEXT: [[APPLY:%.*]] = apply [[FNREF]]()
// CHECK: load
// CHECK: } // end sil function '$s4Test17ExplicitInitClassC1sAA1SVvg'

View File

@@ -10,7 +10,7 @@ func foo() {}
@objc public class C: NSObject {
// C.__allocating_init()
// CHECK-LABEL: sil{{.*}}@$s4Test1CCACycfC
// CHECK: [[FNREF:%.*]] = function_ref @$[[DIAGNOSEFN:ss31_diagnoseUnavailableCodeReacheds5NeverOy(FTwb|F)]] : $@convention(thin) () -> Never
// CHECK: [[FNREF:%.*]] = function_ref @$[[DIAGNOSEFN:(ss36_diagnoseUnavailableCodeReached_aeics5NeverOyF|ss31_diagnoseUnavailableCodeReacheds5NeverOyF)]] : $@convention(thin) () -> Never
// CHECK-NEXT: [[APPLY:%.*]] = apply [[FNREF]]()
// CHECK: {{%.*}} = function_ref @$s4Test1CCACycfcTD
// CHECK: } // end sil function '$s4Test1CCACycfC'

View File

@@ -5,7 +5,7 @@ public struct S {}
extension S: P {}
// CHECK-LABEL: sil{{.*}}@$s4Test27unavailableOpaqueReturnFuncQryF
// CHECK: [[FNREF:%.*]] = function_ref @$[[DIAGNOSEFN:ss31_diagnoseUnavailableCodeReacheds5NeverOy(FTwb|F)]] : $@convention(thin) () -> Never
// CHECK: [[FNREF:%.*]] = function_ref @$[[DIAGNOSEFN:(ss36_diagnoseUnavailableCodeReached_aeics5NeverOyF|ss31_diagnoseUnavailableCodeReacheds5NeverOyF)]] : $@convention(thin) () -> Never
// CHECK-NEXT: [[APPLY:%.*]] = apply [[FNREF]]()
// CHECK: function_ref @$s4Test1SVACycfC
// CHECK: } // end sil function '$s4Test27unavailableOpaqueReturnFuncQryF'

View File

@@ -17,7 +17,7 @@ extension EnumWithProtocolWitness: P {}
// protocol witness for static P.requirement(_:) in conformance EnumWithProtocolWitness
//
// CHECK-LABEL: sil{{.*}}@$s4Test23EnumWithProtocolWitnessOAA1PA2aDP11requirementyxAA1SVFZTW
// CHECK: [[FNREF:%.*]] = function_ref @$[[DIAGNOSEFN:ss31_diagnoseUnavailableCodeReacheds5NeverOy(FTwb|F)]] : $@convention(thin) () -> Never
// CHECK: [[FNREF:%.*]] = function_ref @$[[DIAGNOSEFN:(ss36_diagnoseUnavailableCodeReached_aeics5NeverOyF|ss31_diagnoseUnavailableCodeReacheds5NeverOyF)]] : $@convention(thin) () -> Never
// CHECK-NEXT: [[APPLY:%.*]] = apply [[FNREF]]()
// CHECK: {{%.*}} = function_ref @$s4Test23EnumWithProtocolWitnessO11requirementyAcA1SVcACmF
// CHECK: } // end sil function '$s4Test23EnumWithProtocolWitnessOAA1PA2aDP11requirementyxAA1SVFZTW'

View File

@@ -5,7 +5,7 @@ public struct S {}
@available(*, unavailable)
public struct ImplicitInitStruct {
// CHECK-LABEL: sil hidden {{.*}} @$s4Test18ImplicitInitStructVACycfC
// CHECK: [[FNREF:%.*]] = function_ref @$[[DIAGNOSEFN:ss31_diagnoseUnavailableCodeReacheds5NeverOy(FTwb|F)]] : $@convention(thin) () -> Never
// CHECK: [[FNREF:%.*]] = function_ref @$[[DIAGNOSEFN:(ss36_diagnoseUnavailableCodeReached_aeics5NeverOyF|ss31_diagnoseUnavailableCodeReacheds5NeverOyF)]] : $@convention(thin) () -> Never
// CHECK-NEXT: [[APPLY:%.*]] = apply [[FNREF]]()
// CHECK: return
// CHECK: } // end sil function '$s4Test18ImplicitInitStructVACycfC'

View File

@@ -3,7 +3,7 @@
public struct S {}
// CHECK-LABEL: sil{{.*}}@$s4Test15unavailableFuncAA1SVyF
// CHECK: [[FNREF:%.*]] = function_ref @$[[DIAGNOSEFN:ss31_diagnoseUnavailableCodeReacheds5NeverOy(FTwb|F)]] : $@convention(thin) () -> Never
// CHECK: [[FNREF:%.*]] = function_ref @$[[DIAGNOSEFN:(ss36_diagnoseUnavailableCodeReached_aeics5NeverOyF|ss31_diagnoseUnavailableCodeReacheds5NeverOyF)]] : $@convention(thin) () -> Never
// CHECK-NEXT: [[APPLY:%.*]] = apply [[FNREF]]()
// CHECK-NEXT: unreachable
// CHECK-NEXT: } // end sil function '$s4Test15unavailableFuncAA1SVyF'

View File

@@ -106,14 +106,14 @@ enum HasUnavailableElement: Hashable {
// CHECK-NEXT: case .a:
// CHECK-NEXT: index_a = 0
// CHECK-NEXT: case .b:
// CHECK-NEXT: _diagnoseUnavailableCodeReached()
// CHECK-NEXT: _diagnoseUnavailableCodeReached{{.*}}()
// CHECK-NEXT: }
// CHECK-NEXT: private var index_b: Int
// CHECK-NEXT: switch b {
// CHECK-NEXT: case .a:
// CHECK-NEXT: index_b = 0
// CHECK-NEXT: case .b:
// CHECK-NEXT: _diagnoseUnavailableCodeReached()
// CHECK-NEXT: _diagnoseUnavailableCodeReached{{.*}}()
// CHECK-NEXT: }
// CHECK-NEXT: return index_a == index_b
// CHECK-NEXT: }
@@ -124,7 +124,7 @@ enum HasUnavailableElement: Hashable {
// CHECK-NEXT: case .a:
// CHECK-NEXT: discriminator = 0
// CHECK-NEXT: case .b:
// CHECK-NEXT: _diagnoseUnavailableCodeReached()
// CHECK-NEXT: _diagnoseUnavailableCodeReached{{.*}}()
// CHECK-NEXT: }
// CHECK-NEXT: hasher.combine(discriminator)
// CHECK-NEXT: }
@@ -151,7 +151,7 @@ enum HasAssociatedValuesAndUnavailableElement: Hashable {
// CHECK-NEXT: hasher.combine(0)
// CHECK-NEXT: hasher.combine(a0)
// CHECK-NEXT: case .b:
// CHECK-NEXT: _diagnoseUnavailableCodeReached()
// CHECK-NEXT: _diagnoseUnavailableCodeReached{{.*}}()
// CHECK-NEXT: }
// CHECK-NEXT: }
@@ -163,7 +163,7 @@ enum HasAssociatedValuesAndUnavailableElement: Hashable {
// CHECK-NEXT: }
// CHECK-NEXT: return true
// CHECK-NEXT: case (.b, .b):
// CHECK-NEXT: _diagnoseUnavailableCodeReached()
// CHECK-NEXT: _diagnoseUnavailableCodeReached{{.*}}()
// CHECK-NEXT: default:
// CHECK-NEXT: return false
// CHECK-NEXT: }

View File

@@ -1,5 +1,6 @@
// RUN: %target-swift-frontend -print-ast %s | %FileCheck %s
// RUN: %target-swift-frontend -target %target-cpu-apple-macosx10.51 -print-ast %s | %FileCheck %s
// RUN: %target-swift-frontend -print-ast %s | %FileCheck %s --check-prefixes=CHECK,CHECK-PRE-SWIFT5_9
// RUN: %target-swift-frontend -target %target-cpu-apple-macosx10.51 -print-ast %s | %FileCheck %s --check-prefixes=CHECK,CHECK-PRE-SWIFT5_9
// RUN: %target-swift-frontend -target %target-cpu-apple-macosx14 -print-ast %s | %FileCheck %s --check-prefixes=CHECK,CHECK-SWIFT5_9
// REQUIRES: OS=macosx
// CHECK-LABEL: internal enum HasElementsWithAvailability : Hashable
@@ -29,9 +30,11 @@ enum HasElementsWithAvailability: Hashable {
// CHECK-NEXT: case .alwaysAvailable:
// CHECK-NEXT: index_a = 0
// CHECK-NEXT: case .neverAvailable:
// CHECK-NEXT: _diagnoseUnavailableCodeReached()
// CHECK-PRE-SWIFT5_9-NEXT: _diagnoseUnavailableCodeReached_aeic()
// CHECK-SWIFT5_9-NEXT: _diagnoseUnavailableCodeReached()
// CHECK-NEXT: case .unavailableMacOS:
// CHECK-NEXT: _diagnoseUnavailableCodeReached()
// CHECK-PRE-SWIFT5_9-NEXT: _diagnoseUnavailableCodeReached_aeic()
// CHECK-SWIFT5_9-NEXT: _diagnoseUnavailableCodeReached()
// CHECK-NEXT: case .obsoleted10_50:
// CHECK-NEXT: index_a = 1
// CHECK-NEXT: case .introduced10_50:
@@ -42,9 +45,11 @@ enum HasElementsWithAvailability: Hashable {
// CHECK-NEXT: case .alwaysAvailable:
// CHECK-NEXT: index_b = 0
// CHECK-NEXT: case .neverAvailable:
// CHECK-NEXT: _diagnoseUnavailableCodeReached()
// CHECK-PRE-SWIFT5_9-NEXT: _diagnoseUnavailableCodeReached_aeic()
// CHECK-SWIFT5_9-NEXT: _diagnoseUnavailableCodeReached()
// CHECK-NEXT: case .unavailableMacOS:
// CHECK-NEXT: _diagnoseUnavailableCodeReached()
// CHECK-PRE-SWIFT5_9-NEXT: _diagnoseUnavailableCodeReached_aeic()
// CHECK-SWIFT5_9-NEXT: _diagnoseUnavailableCodeReached()
// CHECK-NEXT: case .obsoleted10_50:
// CHECK-NEXT: index_b = 1
// CHECK-NEXT: case .introduced10_50:
@@ -59,9 +64,11 @@ enum HasElementsWithAvailability: Hashable {
// CHECK-NEXT: case .alwaysAvailable:
// CHECK-NEXT: discriminator = 0
// CHECK-NEXT: case .neverAvailable:
// CHECK-NEXT: _diagnoseUnavailableCodeReached()
// CHECK-PRE-SWIFT5_9-NEXT: _diagnoseUnavailableCodeReached_aeic()
// CHECK-SWIFT5_9-NEXT: _diagnoseUnavailableCodeReached()
// CHECK-NEXT: case .unavailableMacOS:
// CHECK-NEXT: _diagnoseUnavailableCodeReached()
// CHECK-PRE-SWIFT5_9-NEXT: _diagnoseUnavailableCodeReached_aeic()
// CHECK-SWIFT5_9-NEXT: _diagnoseUnavailableCodeReached()
// CHECK-NEXT: case .obsoleted10_50:
// CHECK-NEXT: discriminator = 1
// CHECK-NEXT: case .introduced10_50: